Writing on the principles & practice of building digital products

Cucumber: Getting started

Writing tests will make for better code and a less stressed you. Cucumber is a simple way of testing a Rails app. It’s like clicking through the site to check things are working, except the clicking happens automatically.

Cucumber is an end-to-end approach to testing, which means it uses a website in the same way as a human would (i.e. by navigating to pages and clicking on things). It’s basically like clicking through the website to check things are working as they should, except a computer does the grunt work for you. I'll say now I’m by no means an expert when it comes to Rails testing (far from it), however in learning the basics I’ve found there’s a lot information out there which can be overwhelming for a beginner - it certainly was for me. So to that end so I’ve put together a quick rundown of how to get started and some of the key concepts. Here's what we'll cover:

Setup

Before we get stuck in, I’m assuming you’ve got a Rails project already set up you want to test. Also make sure you have a test database set up in config/database.yml, and that it's not the same one as your development database. The test database will be wiped every time we run our tests so this is important.

First we need to install some gems so we can run our tests. Do this by adding the following to your Gemfile:

# Gemfile
group :test do
  gem 'cucumber-rails', :require => false
  gem 'database_cleaner'
  gem 'capybara'
  gem 'selenium-webdriver'
end

Install the gems by running:

bundle install

Next, run the Cucumber generator. This will set up a bunch of files and folders for you.

rails generate cucumber:install

To run tests in Chrome rather than Firefox (the selenium default), you'll need to install Chromedriver. On OSX you can do this via Homebrew:

brew install chromedriver

Then add the following to the bottom of your env.rb file. This sets up the database to be cleaned between tests and registers Chrome as your default javascript driver.

# features/support/env.rb

# Clean the database before and after each scenario
Before do
  DatabaseCleaner.start
end

After do |scenario|
  DatabaseCleaner.clean
end

# Register Chrome as the default driver
Capybara.register_driver :chrome do |app|
  Capybara::Selenium::Driver.new(app, :browser => :chrome)
end

Capybara.javascript_driver = :chrome

Let's go through how the tests are structured and the various files involved.

How tests are structured

There are two main types of file which make up a Cucumber test: feature files and step definitions. Feature files contain human readable tests, whereas step definitions translate these features into actions a server or browser can perform.

Both these file types, along with a couple of other supporting files, are held in a folder called /features in the root of your Rails project. These folders are automatically generated by rails generate cucumber:install you ran above. By way of example, say you had a contact form on your site you wanted to test - the folder structure might look something like this:

/features
  /step_definitions
    contact_form_steps.rb
  /support 
    env.rb
  contact_form.feature

In this example, contact_form.feature contains the scenarios you want to test, and step_definitions/contact_form_steps.rb contains the ruby code which will make those scenarios actually do something. You need to manually created these feature and step definition files when making new features. Let's go through what each one does in a bit more detail:

Feature files

Features are text files with a .feature extension, written in a syntax called Gherkin. Gherkin is the language you’ll use to describe what you want your tests to do. You’ll then write Ruby code to translate the features you write in Gherkin to something a computer understands. A very simple example feature file might look something like this:

# features/contact_form.feature
Feature: Contact form

  Scenario: Send an email via the contact form
    Given I am on the contact page
    When I fill in the contact form 
    Then I should see a thank you message

The Gherkin syntax is geared around the "Given, When, Then" format. The idea is that writing tests this way gives you a consistent template for writing easy to understand test criteria. It's a subject worthy of it's own post, but for now you can find more information on the thinking behind it over on the Cucumber Wiki.

Step definitions

Step definitions are Ruby files which interpret your Gherkin steps. As mentioned above, they take a sentence a human can understand and turn it into something a computer can understand. For example, to translate the above scenario, the step definitions could look something like this:

# features/step_definitions/contact_form_steps.rb
Given(/^I am on the contact page$/) do
  visit('/contact')
end

When(/^I fill in the contact form$/) do
  fill_in('Message', :with => 'Hello there!')
  find('Submit').click
end

Then(/^I should see a thank you message$/) do
  page.has_content?("Thank you")
end 

The language we’re using to make this work is called Capybara. With it you can visit urls, find and interact with page elements, assert that elements have content - basically do anything a person would do when they use a website. There’s a helpful cheat sheet on Github with a rundown of the basic tasks.

Running your tests

Tests are run via the command line. There are a few different ways to run tests.

To run all your scenarios in one go, use the following:

bundle exec cucumber

To just run one specific scenario, you can refer to it by name. So for our example scenario above you could run:

bundle exec cucumber -n "Send an email via the contact form"

Needless to say this is much quicker than running all your tests every time and is very useful in active development.

A small footnote, if you're using the foreman gem to load environment variables, you should prepend foreman run to your test invocations, e.g:

foreman run bundle exec cucumber -n "Send an email via the contact form"

When you run your tests, they will either open a real browser window just as you would see when using a website, or they can run in what's called a "headless" browser. Headless browsers run in the background and don't open an actual window, so are faster than booting up a browser. However their main drawback is that they can't run any Javascript on your page. If you need to test a site which requires Javascript to work properly (as most of ours do), then you can put what's called a "tag" at the beginning of your feature file. Here we've included the @javascript tag open both scenarios in the feature in a web browser:

# features/contact_form.feature
@javascript
  Feature: Send email via API

  # Both scenarios will open an actual web browser
  Scenario: Send plain text email 
    #...
  Scenario: Send HTML email 
    #...

You can also just include tags for a single Scenario. This is useful if you only need to open a browser for one scenario in a feature.

# features/contact_form.feature
Feature: Send email via API

  @javascript
  Scenario: Send plain text email 
    # will open a web browser

  Scenario: Send HTML email 
    # will run in a headless browser (i.e. you won't see it)

When you run your tests you'll see a summary explaining the outcome of your test, which will look something like the following:

Given I am on the contact page        
When I fill in the contact form       
Then I should see a thank you message 

1 scenario (1 passed)
3 steps (3 passed)
0m10.242s

As you can see, the output will tell us which of our steps passed and how long the test took. Also if there are errors they'll pop up here with handy line numbers so we've got somewhere to start debugging from.

Get testing

This is just skimming the surface of testing your code but hopefully it's enough to get started with. There'll be more in this series on how to handle test data and when to write tests in your workflow, but to begin with it's helpful just to write a couple of tests on an existing project to get a feel for how they work. Once you feel the satisfaction of having a test pass, you'll be hooked.

Resources