Running feature specs with Capybara and Chrome headless

July 05, 2017 | Tim Petricola | 2-minute read

At Drivy, we’ve been using Capybara and PhantomJS to run our feature specs for years. Even with its issues, PhantomJS is a great way to interact with a browser without starting a graphical interface. Recently, Chrome added support for a headless flag so it could be started without any GUI. Following this announcement, the creator of PhantomJS even announced that he would be stepping down as a maintainer.

Setting feature specs to run with a headless version of Chrome means that our features specs can be executed in the same environment most of our users are browsing with. It is also supposed to improve memory usage and stability.

Installing prerequisites dependencies

Assuming you already have Chrome (59 or more recent for macOS/Linux, 60 or more recent for Windows) on your machine, you’ll also need to install ChromeDriver. On macOS, you can install it with homebrew:

brew install chromedriver

If not already present in your application, add selenium-webdriver to your Gemfile:

group :test do
  gem 'selenium-webdriver'
end

Configuring Capybara

Capybara provides a simple API to register a custom driver. You can do so in your test/spec helper file.

Capybara.register_driver(:headless_chrome) do |app|
  capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
    chromeOptions: { args: %w[headless disable-gpu] }
  )

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

As stated in the documentation, the disable-gpu is needed to run Chrome as headless.

Using Chrome headless

On an app running on Rails 5.1 with system test cases, use the provided DSL to use the driver:

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :headless_chrome
end

Otherwise, use the more generic way of setting a javascript driver for Capybara:

Capybara.javascript_driver = :headless_chrome

Troubleshooting

Empty screenshots

With Capybara, there is a possibility to take a screenshot during your tests (or automatically on a failure). This feature results in an empty gray image on headless Chrome 59 but the proper behavior is restored on Chrome 60 (in beta as of today).

trigger method

To prevent some issues in PhantomJS when elements would overlap, we had a lot of calls like this:

find('.clickable_element').trigger('click')

In Chrome, it is raising the following error as the trigger method is not supported:

Capybara::NotSupportedByDriverError: Capybara::Driver::Node#trigger

This can now safely be replaced by the straightforward click method:

find('.clickable_element').click

Conclusion

You can see an example app on drivy/rails-headless-capybara.

Even though we introduced Chrome headless very recently, we’re quite optimistic that it will lead to even less bugs in our application.

View openings 👍  Like this post? Join Drivy's engineering team!