Category Archives: Cucumber

The difference between RSpec, Capybara and Cucumber

If you’re new to Rails testing you’ve probably come across the terms RSpec, Capybara and Cucumber.

All three are testing tools. What are they for? Do you need all of them? Here are some answers.

RSpec

RSpec is a testing framework. It’s what allows you to write and run your tests.

An analogous tool would be MiniTest. In my experience, most commercial Rails projects use RSpec and most open-source Ruby projects use MiniTest. At any Rails job you’re more likely to be using RSpec than MiniTest. (I’m not sure why this is the way it is.)

Capybara

Some Rails tests operate at a “low level”, meaning no browser interaction is involved. Other “high level” tests do actually spin up a browser and click links, fill out form fields, etc.

Low-level tests can be executed with just RSpec and nothing more. But for tests that use the browser, something more is needed.

This is where Capybara comes into the picture. Capybara provides helper methods like fill_in to fill in a form field, click_on to click a button, etc.

Please note that Capybara does NOT have to be used in conjunction with Cucumber. It’s completely possible to write integration tests in Rails with just RSpec and Capybara.

Cucumber

Cucumber is a tool for writing test cases in something close to English. Here’s an example from Wikipedia:

Scenario: Eric wants to withdraw money from his bank account at an ATM
    Given Eric has a valid Credit or Debit card
    And his account balance is $100
    When he inserts his card
    And withdraws $45
    Then the ATM should return $45
    And his account balance is $55

Cucumber can be connected with RSpec and Capybara and used to write integration tests.

My personal take on Cucumber is that while the English-like syntax might appear clearer at first glance, it’s actually less clear than bare RSpec/Capybara syntax. (Would a Ruby class be more understandable if it were English instead of Ruby?)

Cucumber adds both a layer of mental overhead and a layer of maintenance overhead on top of the RSpec + Capybara combination. I always try as hard as I can to try to steer new testers away from Cucumber.

“So, what should I use to test my Rails apps?”

My advice is to use the combination of RSpec + Capybara and forget about Cucumber. What if you don’t know where to start with writing RSpec/Capybara tests? If that’s the case, you might like to check out my guide to RSpec + Capybara testing, which includes a tutorial.

Why I recommend against using Cucumber

Around the time I first started using Rails in 2011, I noticed that a lot of developers, seemingly all Rails developers, were using Cucumber to assist with testing.

I bought into the idea—describing test cases in plain English—but in practice I found Cucumber not to be very valuable. In fact, my experience has been that Cucumber adds a negative amount of value.

In recent years I’ve noticed (although this is anecdotal and might just be my perception) that fewer codebases seem to use Cucumber and that fewer Rails developers seem to be on board with Cucumber. I had thought Cucumber was pretty much dead. But lately, to my surpise, I’ve seen Cucumber recommended to testing noobs more than a few times. Since I consider Cucumber to be a bad thing, I want to explain why I think so and why I don’t think other people should use it.

In my view there are two general ways Cucumber can be used: it can be used as intended or it can be abused. In the former case, I believe Cucumber has a small negative value. In the latter case I believe it has a large negative value.

Why Cucumber is bad when it’s not used as intended

Most production Cucumber scenarios I’ve seen look something like this:

Given a user exists with email "test@example.com" and password "mypassword"
And I visit "/sign_in"
And I fill in the "Email" field with "test@example.com"
And I fill in the "Password" field with "mypassword"
And I click "Sign In"
And I visit "/user/edit"
And I fill in the "First Name" field with "John"
And I fill in the "Last Name" field with "Smith"
And I fill in the "Age" field with "30"
And I click "Save"
And I visit "/profile"
Then I should see "John Smith, 30"

These kinds of tests, with fine-grained steps, arise when the developers seem to mistake Cucumber for a way to write Ruby in English. The above scenario provides exactly zero benefit, in my opinion, over the following equivalent Capybara scenario:

FactoryBot.create(:user, email: 'test@example.com', password: 'mypassword')

visit sign_in_path
fill_in 'Email', with: 'test@example.com'
fill_in 'Password', with: 'mypassword'
click_on 'Sign In'

visit edit_user_path
fill_in 'First Name', with: 'John'
fill_in 'Last Name', with: 'Smith'
fill_in 'Age', with: '30'
click_on 'Save'

visit profile_path
expect(page).to have_content('John Smith, 30')

The Cucumber/Gherkin version is no shorter nor more easily understandable.

To be fair to Cucumber, nobody who understands Cucumber advocates writing Cucumber scenarios in this way. The Cucumber creator himself, Aslak Hellesøy, wrote a post in 2011 saying not to do this. Other people have written similar things.

I think it’s telling that so many people have written blog posts advising against the very common practice of writing fine-grained Cucumber steps. To me it’s kind one of those gas station doors that looks for all the world like a pull door, so every single person who comes up to it pulls it instead of pushes it, feels like a dumbass, and then pushes it. So the gas station manager puts up a big sign that says “PUSH”, but most people don’t notice it and the problem persists. What instead should have been done is to make the push door look like a push door, without the big useless handle that you’re not supposed to pull. I get that the Cucumber maintainers tried to do that by removing `web_steps.rb`, but in my experience it didn’t seem to work.

And it doesn’t matter much anyway because Cucumber still sucks even if you don’t abuse it by writing fine-grained steps. I’ll explain why I think so.

Why Cucumber is bad even when it is used as intended

Here’s a version of the above Cucumber scenario that’s done in the way the Cucumber creators would intend. There are two parts.

First, the Gherkin steps:

Given I am signed in
And I provide my name and age details
Then I should see those details on my profile page

Second, the underlying Ruby steps:

Given /^I am signed in$/ do
  visit sign_in_path
  fill_in 'Email', with: 'test@example.com'
  fill_in 'Password', with: 'mypassword'
  click_on 'Sign In'
end

And /^I provide my name and age details$/ do
  visit edit_user_path
  fill_in 'First Name', with: 'John'
  fill_in 'Last Name', with: 'Smith'
  fill_in 'Age', with: '30'
  click_on 'Save'
end

Then /^I should see those details on my profile page$/ do
  visit profile_path
  expect(page).to have_content('John Smith, 30')
end

This is actually pretty decent-looking and appealing, at least at first glance. There are two problems, though. First, this way of doing things doesn’t really provide any clarity over doing it the Capybara way. Second, the step definitions usually end up in a single, flat file full of “step soup” where unrelated steps are mixed together willy-nilly.

Compare this again with the Capybara version:

FactoryBot.create(:user, email: 'test@example.com', password: 'mypassword')

visit sign_in_path
fill_in 'Email', with: 'test@example.com'
fill_in 'Password', with: 'mypassword'
click_on 'Sign In'

visit edit_user_path
fill_in 'First Name', with: 'John'
fill_in 'Last Name', with: 'Smith'
fill_in 'Age', with: '30'
click_on 'Save'

visit profile_path
expect(page).to have_content('John Smith, 30')

The sign in portion is usually abstracted away in Capybara, too, so the scenario would look more like this:

FactoryBot.create(:user, email: 'test@example.com', password: 'mypassword')

sign_in

visit edit_user_path
fill_in 'First Name', with: 'John'
fill_in 'Last Name', with: 'Smith'
fill_in 'Age', with: '30'
click_on 'Save'

visit profile_path
expect(page).to have_content('John Smith, 30')

That’s not too crazy at all. In order for Cucumber to be a superior solution to using bare Capybara, it would have to have some pretty strong benefits to compensate for the maintenance burden and cognitive overhead it adds. But it doesn’t.

So what do I recommend doing instead of using Cucumber? I think just using Capybara by itself is fine, and better than using Capybara + Cucumber. I also think Capybara + page objects is a pretty good way to go.