Configuring Rails system tests for headless and headfull browsers

Want to run your system tests headless? And why not both ways? Here’s how to extend Rails tasks to run your system tests with the driver of your choice.

Rails 6 came with system tests baked-in, and so if you generate a new Rails app today, you end up with the following setup code:

# test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
end

You’ll need some dependencies for this to work. If you don’t have them, add the following to your Gemfile:

# Gemfile
...
group :test do
  # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
  gem "capybara", ">= 3.26"
  gem "selenium-webdriver", ">= 4.0.0"
  gem "webdrivers"
end

A lot of people want to switch the default driver to something else, especially to the headless Chrome for faster tests.

It’s surprisingly easy. You only need to replace the driver’s name in using parameter:

# test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end

But by doing this change, you lost the ability to watch your tests visually. So why not have both?

Let’s set the driver based on DRIVER environment variable:

# test/application_system_test_case.rb
require "test_helper"

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  DRIVER = if ENV["DRIVER"]
    ENV["DRIVER"].to_sym
  else
    :headless_chrome
  end

  driven_by :selenium, using: DRIVER, screen_size: [1400, 1400]
end

I kept headless Chrome as default as something you want to run in CI.

To run system tests with a different driver, we just set that variable on the command line:

$ DRIVER=chrome rails test:system

Pretty nice, yet we can do more. We can have a fancy new Rake task to do this job for us:

# lib/tasks/test.rake
namespace :test do
  namespace :system do
    task :with, [:driver] => :environment do |task, args|
      ENV["DRIVER"] = args[:driver]
      Rake::Task["test:system"].invoke
    end
  end
end

This task sets the ENV variable for us and then invokes the regular Rails’ test:system task. Nothing less, nothing more.

By defining the driver argument, we can now choose the driver nicely on the command line:

$ rails test:system:with[chrome]
$ rails test:system:with[firefox]
$ rails test:system:with[headless_chrome]

If, on the other hand, we want to define exact tasks for particular drivers, we can do this too:

# lib/tasks/test.rake
namespace :test do
  namespace :system do
    task :chrome => :environment do |task, args|
      ENV["DRIVER"] = "chrome"
      Rake::Task["test:system"].invoke
    end
  end
end

Then we can run the test:system:chrome task for the headfull Chrome:

$ rails test:system:chrome

And that’s it! Develop with headless browsers and admire your work once in a while with a full experience!

← IT'S OUT NOW

I wrote a complete guide on web application deployment. Ruby with Puma, Python with Gunicorn, NGINX, PostgreSQL, Redis, networking, processes, systemd, backups, and all your usual suspects.

More →