How to run Capybara feature specs with Selenium and headless Chrome — CircleCI 2.0 case study

Artur Trzop
Mar 10, 2018 · 4 min read

I’ve been using Capybara-WebKit for a long time but while switching from CircleCI 1.0 to CircleCI 2.0 I had some problems to use it on the CI.

This triggered to try Chrome Headless with Selenium Webdriver. I will show you how to configure Circle CI 2.0 and your Ruby on Rails project to use capybara/selenium/chrome headless together.

Add capybara and selenium-webdriver

Let’s add capybara and selenium-webdriver gems to Gemfile:

# Gemfile
group :development, :test do
gem 'capybara'
gem 'selenium-webdriver'

and run bundle install.

If you already had the gems in your Gemfile then ensure you have latest version with bundle update capybara selenium-webdriver.

If you want to make sure Capybara feature specs will work on your development machine:

$ brew install chromedriver

If your feature specs fail then upgrade the driver because you may have installed old one.

$ brew upgrade chromedriver

Configure Capybara

Add config file for Capybara:

# spec/support/config/capybara.rb
JS_DRIVER = :selenium_chrome_headless

Capybara.default_driver = :rack_test
Capybara.javascript_driver = JS_DRIVER
Capybara.default_max_wait_time = 2

RSpec.configure do |config|
config.before(:each) do |example|
Capybara.current_driver = JS_DRIVER if example.metadata[:js]
Capybara.current_driver = :selenium if example.metadata[:selenium]
Capybara.current_driver = :selenium_chrome if example.metadata[:selenium_chrome]

config.after(:each) do

Ensure you load config files from spec/support directory:

# spec/rails_helper.rb

# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

Example feature spec

We can create example feature spec to test if everything works:

# spec/features/home_spec.rb
feature 'Homepage Features' do
before { visit root_path }

# it won't run js code but it is fast
it do
expect(page).to have_content 'Hello World'

# it will run js code
it '', :js do
expect(page).to have_content 'Hello World'

# it will open Firefox
# remove x from xit to run the test in Firefox on your machine to preview
xit '', :selenium do
expect(page).to have_content 'Hello World'

# it will open Chrome
# remove x from xit to run the test in Chrome on your machine to preview
xit '', :selenium_chrome do
expect(page).to have_content 'Hello World'

And run tests on your development machine with bin/rspec spec/features/home_spec.rb or bundle exec rspec spec/features/home_spec.rb.

Here is example .circleci/config.yml:

# .circleci/config.yml
version: 2
parallelism: 1
working_directory: ~/project-name
# this is important to use proper image with browsers support
- image: circleci/ruby:2.5.0-node-browsers
PGUSER: project-name
- image: circleci/postgres:9.4.12-alpine
POSTGRES_DB: project-name_test
POSTGRES_USER: project-name
- image: redis:3.2.7
- checkout

# Restore bundle cache
- type: cache-restore
# remove space between { {
key: project-name-{ { checksum "Gemfile.lock" }}

# Bundle install dependencies
- run: bundle install --path vendor/bundle

# Store bundle cache
- type: cache-save
# remove space between { {
key: project-name-{ { checksum "Gemfile.lock" }}
- vendor/bundle

# Prepare .env, useful if you use dotenv gem
- run: cp .env.example .env

# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load

# Run rspec in parallel
- type: shell
command: |
bundle exec rspec --profile 10 \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress

# Save artifacts
- type: store_test_results
path: /tmp/test-results

Speed up your tests with Circle CI parallelisation

If your feature specs are very long you can save some time by running multiple parallel CI nodes. For instance set it to 6 in .circleci/config.yml and use dynamic RSpec specs allocation across CI nodes with knapsack_pro gem and Queue Mode to get optimal test suite split to save as much time as possible.

# .circleci/config.yml
parallelism: 6

# some tests that are not balanced and executed only on first CI node
- run: case $CIRCLE_NODE_INDEX in 0) npm test ;; esac

# auto-balancing CI build time execution to be flat and optimal (as fast as possible).
# Queue Mode does dynamic tests allocation so the previous not balanced run command won't
# create a bottleneck on the CI node
- run:
name: RSpec via knapsack_pro Queue Mode
command: |
# export word is important here!
export RAILS_ENV=test
bundle exec rake "knapsack_pro:queue:rspec[--format documentation]"

You can learn how RSpec test suite parallelisation works in 1 minute video.

Now you are good to push your code to a repository and see how your Capybara feature specs work with Chrome Headless on CircleCI 2.0.

Originally published at

Artur Trzop

Written by

I work with Ruby and Elixir. I run I write blog posts at and

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade