Skip to content

Instantly share code, notes, and snippets.

@deanmarano
Last active July 22, 2023 17:26
Show Gist options
  • Save deanmarano/aeae5cd2d357fec1b06e30ead397d4e3 to your computer and use it in GitHub Desktop.
Save deanmarano/aeae5cd2d357fec1b06e30ead397d4e3 to your computer and use it in GitHub Desktop.
Tested Jekyll

Testing Your Jekyll Site, The Ruby Way

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.

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 - 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?

Enter rack-jekyll

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

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

Setting Up RSpec

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:

spec/spec_helper.rb
# 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

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.

spec/sample_spec.rb
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.

Finale

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!

@jamogriff
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment