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.
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
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.
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
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
methodTo 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
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.