Acceptance Testing with RSpec, Capybara, and Selenium
To create real world acceptance tests for our site, we used a few technologies we were familiar with:
- RSpec — Behavior Driven Development for Ruby
- Capybara — Testing of web applications via simulation of a real user’s interaction with your app
- Selenium — Browser automation
This trifecta sets up a way to write tests (RSpec) and drive the browser (Selenium) using a acceptance test API (Capybara). This gets us most of the way there, but we’re left with one problem — how do we serve the static site, so we can actually test it?
Most popular Ruby applications (such as Ruby on Rails and Sinatra) use the Rack interface to connect web requests to Ruby code. Since Jekyll generates static pages, it doesn’t have a Rack interface by default — however Rack-Jekyll is a gem that bridges the gap, and allows us to serve our pages to Selenium so we can test it using the browser.
Testing Jekyll, From Scratch
Starting from a Jekyll site, we’ll walk through the steps in getting setup with your first test. For the purposes of this example, we’ll start with a clean Jekyll site.
jekyll new tested-jekyll
Setting Up Your Gemfile
If you don’t already have one, we’re going to create a Gemfile so we can include additional gems.
source 'https://rubygems.org'gem "jekyll"group :development, :test do
After you’ve created this, run bundle install to install the gems.
Setting Up RSpec
Next, we’ll generate the necessary configuration files for RSpec using rspec — init.
$ rspec --init
This creates two files — .rspec is a configuration file for RSpec output, and spec/spec_helper.rb is the file that loads your tests and configures the environment. To setup RSpec to use Capybara and Rack-Jekyll, we’ll need to modify this file as noted:
# Require all of the necessary gems
require 'pry'RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end # Configure Capybara to use Selenium.
Capybara.register_driver :selenium do |app|
# Configure selenium to use Chrome.
Capybara::Selenium::Driver.new(app, :browser => :chrome)
end # Configure Capybara to load the website through rack-jekyll.
# (force_build: true) builds the site before the tests are run,
# so our tests are always running against the latest version
# of our jekyll site.
Capybara.app = Rack::Jekyll.new(force_build: true)
Note: For brevity, all generated comments have been removed
Writing Your First Test
Create a file in the spec/ directory called sample_spec.rb. The *_spec.rb suffix is needed for all files you want RSpec to run automatically.
describe "sample", type: :feature, js: true do
it "has the page title" do
# `binding.pry` is useful for crafting the right selector
# or checking the actual state of the page
binding.pry # test will pause here
expect(find('.post-link').text).to eq('Welcome to Jekyll!')
Then run bundle exec rspec. This will
- Build your Jekyll site
- Open a Chrome browser
- Run your tests
- Pause after the page loads.
$ bundle exec rspec
Configuration file: /Users/dean/github/espark/tested-jekyll/_config.yml
Generating site: /Users/dean/github/espark/tested-jekyll -> /Users/dean/github/espark/tested-jekyll/_siteFrom: /Users/dean/github/espark/tested-jekyll/spec/sample_spec.rb @ line 4 : 1: describe "sample", type: :feature, js: true do
2: it "has the page title" do
3: visit '/'
=> 4: binding.pry
5: expect(find('.post-link').text).to eq('Welcome to Jekyll!')
7: end pry(#<RSpec::ExampleGroups::Sample>)> exit
.Finished in 4.16 seconds (files took 0.88228 seconds to load)
1 example, 0 failures
I’ve left a binding.pry debugger point, which will allow you to interact with the page using Capybara’s find, find_all, click, etc. For more documentation on how to use Capybara to interact with your page, check out the Domain Specific Language (DSL) documentation.
To exit pry, you can use ctrl-d or type exit. The test should run, and complete successfully. You can remove the binding.pry to have the tests run without user interaction.
If all went well, you should have a complete acceptance testing framework for your Jekyll site. From here, it’s easy to set up with your Continuous Integration testing platform (we use CircleCI) to run tests on every branch before merging.
We’ve been using this to ensure our autocomplete forms work as expected, our modals don’t break as we refactor code, and that our new features work as expected. You can find an example repo on Github at espark/tested-jekyll. Happy testing!