Here at eSpark Learning, we use Jekyll to host our marketing site, https://www.esparklearning.com. Within eSpark Engineering, we love automated testing - most of our codebases require a passing test suite for all changes. As we add more javascript to our Jekyll site, we wanted to add a test framework that would give us real world tests - testing that the HTML was valid was no longer enough.
To create real world acceptance tests for our site, we used a few technologies we were familiar with:
- RSpec - Behaviour Driven Development for Ruby
- Capybara - Test web applications by simulating how a real user would interact with your app
- Selenium - Selenium automates browsers
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 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.
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
cd tested-jekyll
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
gem "rspec"
gem "selenium-webdriver"
gem "chromedriver-helper"
gem "capybara"
gem "rack-jekyll"
gem "pry"
end
Note: pry is a debugger that we use for debugging Ruby, and chromedriver-helper allows us to use the Google Chrome browser instead of the default Firefox browser.
After you've created this, run bundle install
to install the gems.
Next, we'll generate the necessary configuration files for RSpec using rspec --init
.
$ rspec --init
create .rspec
create spec/spec_helper.rb
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 'rspec'
require 'capybara/rspec'
require 'rack/jekyll'
require 'rack/test'
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)
end
Note: For brevity, all generated comments have been removed
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
visit '/'
# `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!')
end
end
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/_site
From: /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!')
6: end
7: end
[1] 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 setup with your Continuous Integration testing platform (we use CircleCI) to run tests on every branch before merge.
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!
Thanks for writing this! 🙏 I appreciate you documenting use of RSpec for Jekyll and I don't think I would have caught needing the
rack-jekyll
dependency on my own.