System Tests with RSpec, Rails, Capybara, and Devise

February 14, 2018

Hey there! The winds have blown me in a new direction and this site is now just an archive for posterity's sake. All the new action will be happening at StayWildGames.com. I'll have a writeup detailing the move soon.

Since the release of RSpec 3.7 we’ve been able to take advantage of system tests which are new as of Rails 5.1. If you’ve been using RSpec for a while you’re already likely familiar with feature tests using Capybara. For those new to these types of tests: they allow us to test the actual actions of a user. Clicking, filling out forms, etc.

Going forward it sounds like the RSpec crew has blessed the system moniker and is encouraging its usage going forward. That being said, there are some differences in choosing to use system over feature you should be aware of.

Configuration

Setting up RSpec system tests is going to make it easier when it comes to configuring the Capybara browser.

You’ll need to configure a few things in /spec/rails_helper.rb. I’m currently running a Rails 5.2.rc1 app, so your configs could be slightly different depending on which release you’re running.

Gemfile

Let’s make sure we’ve got the right gems in place to make all this work.

group :test do
  gem 'capybara', '~> 2.15'
  gem 'selenium-webdriver'
  gem 'chromedriver-helper'
end

Don’t forget to bundle install.

Register the drivers

Headless Chrome is a fairly new (and pretty great) way to do automated browser testing. Headless means we don’t have to sit there and watch a browser window get spawned and do a bunch of clicks and whatnot. It’s faster to let it do all that stuff behind the scenes.

# /spec/rails_helper.rb

RSpec.configure do |config|
  ...
  
  Capybara.register_driver :chrome do |app|
    Capybara::Selenium::Driver.new(app, browser: :chrome)
  end

  Capybara.register_driver :headless_chrome do |app|
    capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
      chromeOptions: { args: %w(headless disable-gpu window-size=1024,768) }
    )

    Capybara::Selenium::Driver.new app,
                                   browser: :chrome,
                                   desired_capabilities: capabilities
  end
end

Now, even though headless testing is faster than watching it live it’s still slower than we’d like. So let’s turn if off completely if we don’t need to test javascript.

# /spec/rails_helper.rb

RSpec.configure do |config|
  ...

  # Ensures all non-javascript tests will use the faster :rack_test
  config.before(:each, type: :system) do
    driven_by :rack_test
  end

  # Ensures that all javascript tests use :headless_chrome
  config.before(:each, type: :system, js: true) do
    # Can be switched to :chrome if you want to see it working
    driven_by :headless_chrome
  end
end

Here’s an example of a test that’s going to use javascript and therefore trigger the use of headless Chrome. In this case we’re not testing anything javascripty, but notice the use of js: true.

scenario 'visits home page', js: true do
  visit root_path
  expect(page).to have_content('Hello World!')
end

An example test

# /spec/system/home_spec.rb
require 'rails_helper'

RSpec.describe 'Home', type: :system do
  scenario 'visits home page' do
    visit root_path
    expect(page).to have_content 'Hello World!'
  end
end

This is a goofy test that doesn’t exert the capabilities of Capybara. Head over here to get a start on all it can do.

Final notes

  • RSpec doesn’t seem to have a generator yet for system tests. In other words you’re not going to be able to do rails g rspec:system foobar