Rails system tests with RSpec
Prior to RSpec 3.7 feature specs were the way to go for full-stack testing of application interactions involving javascript in a real/headless browser environment. Recently released RSpec 3.7 added support for system specs based on Rails system tests. ActionDispatch::SystemTestCase
was introduced in Rails 5.1 and provides preconfigured Capybara
wrapper for real browser testing. Having preconfigured Capybara with commonly used features baked right in the framework means much less manual configuration hassle which often was tricky to set up properly. The advantages of using system specs over feature specs are the following:
- Database changes are automatically rolled back after a test and there is no need to manually configure and use database_cleaner strategies.
- More convenient way to switch a browser driver for each spec individually with
driven_by
. - Automatic browser screenshots on failure displayed right in the terminal. Again, that’s preconfigured, no need to add supporting gem like
capybara-screenshot
.
Given the above, it’s also worth emphasizing that the RSpec team now recommends system specs over feature specs for Rails 5.1 and above.
Setting up RSpec system specs with headless Chrome
Let’s start by creating a new rails project. We’re going to exclude the default Rails Minitest integration with --skip-test
as we’re going to use RSpec.
Except for adding rspec-rails
to Gemfile
, the most challenging part of the setup may be to figure out which gems are required for headless Chrome browser testing. Here is list the of required gems:
Make sure that chromedriver
is installed:
Generate spec/test_helper.rb
and spec/rails_helper.rb
:
Writing system spec
Given we have a simple home#index
action, configured as root in routes.rb
, that renders app/views/home/index.html.erb
:
we can implement the first system spec spec/system/home_spec.rb
as follows:
We’re setting :selenium_chrome_headless
driver as we want to use headless Chrome. Other registered drivers provided by Capybara are: :rack_test
, :selenium
and :selenium_chrome
. See the driven_by documentation for more advanced configuration options, e.g. browser resolution.
Let’s see if it works:
Obviously we don’t want to set the driver in each spec. We can configure a global default in spec_helper.rb
. For example:
and override the default driver in individual spec files when needed.
Browser screenshots on failure
It’s worth mentioning that in case of spec failure, system spec automatically takes the browser screenshot and displays it right in the terminal output which is a very handy feature.
That’s the feature baked in Rails system tests. There is no need to configure it manually as it was in case of feature specs, for example with supporting capybara-screenshot
gem.
Javascript testing
So far we’ve only tested server-side rendered content. Let’s add some client-side javascript to prove that indeed system spec is using javascript capable browser (headless Chrome in our case):
After modifying the spec assertion to expect(page).to have_content 'Hello Universe'
, we still get the passing spec testing client-side javascript changes:
Automatic database rollback
As mentioned before, database changes in system specs are automatically rolled back. Let’s see if it’s the case by adding some database output in our page under test:
Modify the spec to seed a record and add new spec example asserting rendered record count:
and the rspec
result is:
That’s great! We no longer need to configure database_cleaner
to clean database state after spec example run as it was previously required in case of feature specs.
The source code of the demonstrated example can be found on GitHub.