Capybara with Given/When/Then steps in acceptance testing

Capybara with Given/When/Then steps in acceptance testing

Capybara with Given/When/Then steps in acceptance testing.Many of us use cucumber to write acceptance tests. We must to say it’s very nice tool that allows you to write features in English plain text. The main goal is provide interface that should be clear for Customer(non-programmer) and Developer.

Cucumber example:

Feature: User signup
  As a user
  I want to sign in
  So I can use service features

  Background:
    Given user with "jack@daniles.com" email and "qwerty" password

  Scenario: Signing in with correct credentials
    When I go to sign in page
    And I fill in "email" with "jack@daniles.com"
    And I fill in "password" with "qwerty"
    And I click "Login" button
    Then I should see "Welcome, jack@daniles.com!"

  Scenario: User tries to sign in with incorrect password
    When I go to sign in page
    And I fill in "email" with "jack@daniles.com"
    And I fill in "password" with "bla"
    And I click "Login" button
    Then I should see "Invalid credentials"
It’s very readable. But cucumber has one big downside:
Steps maintenance
All steps are global and a instance variable from one step is accessible in another step. In big project it can easy lead to mess. Also you can read some rants about cucumber: Previously we also use cucumber as tool for user stories. Lately we found that our customers NEVER read cucumber features. So do we need it anymore? Let’s check alternatives.As you probably know, cucumber use internally Capybara for steps implementation. And there is even more – latest version of Capybara comes with a built in DSL for creating descriptive acceptance tests!So we decided to switch to pure rspec+capybara testing. We moved our features to spec/acceptance directory. Let’s look at rspec+capybara example.

RSpec+Capybara example

$ cat spec/acceptance/signup_feature_spec.rb
feature "User signup" do
  background do
    @user = Factory(:user, :email => 'jack@daniles.com', :password => 'qwerty')
  end

  scenario "Signing in with correct credentials" do
    page.visit "/sessions/new"
    page.fill_in "email", :with => "jack@daniles.com"
    page.fill_in "password", :with => "qwerty"
    page.click_button "Login"
    page.should have_content("Welcome, jack@daniles.com!")
  end

  scenario "User tries to sign in with incorrect password" do
    page.visit "/sessions/new"
    page.fill_in "email", :with => "jack@daniles.com"
    page.fill_in "password", :with => "bla"
    page.click_button "Login"
    page.should have_content("Invalid credentials")
  end
end
Thus approach has one big benefit:
Feature description and feature implementation are located together.
You don’t need anymore to jump between feature and step files to guess where is implementation for a step. It’s nice but there are also two downsides.First downsides is that RSpec documentation output will be too poor and it’s not enough informative:
$ rspec -fs -c spec/acceptance/signup_feature_spec.rb

User signup
  Signing in with correct credentials
  User tries to sign in with incorrect password

Finished in 13.55 seconds
2 examples, 0 failures
Second downside is that scenario does not have anymore exact boundary blocks or steps. It makes harder to understand the scenario flow.Real user story often has big scenarios and we realized that cucumber steps were cool.There were two ways:
  • switch back to cucumber
  • go further and invent
Railswarians never go back :)

Our Solution

If you decided to switch from Cucumber to Capybara acceptance testing try rspec-example steps gem.With this gem you may use Given/When/Then steps into rspec example!

RSpec+Capybara+Steps example

feature "User signup" do
  background do
    @user = Factory(:user, :email => 'jack@daniles.com', :password => 'qwerty')
  end

  Steps "Signing in with correct credentials" do
    When "I go to sign in page" do
      page.visit "/sessions/new"
    end
    And "I fill in right email" do
      page.fill_in "email", :with => @user.email
    end
    And "I fill in  right password" do
      page.fill_in "password", :with => "qwerty"
    end
    And "I click login" do
      page.click_button "Login"
    end
    Then "I should see greeting" do
      page.should have_content("Welcome, #{@user.email}!")
    end
  end

  Steps "User tries to sign in with incorrect password" do
    When "I go to sign in page" do
      page.visit "/sessions/new"
    end
    And "I fill in right email" do
      page.fill_in "email", :with => @user.email
    end
    And "I fill in wrong password" do
      page.fill_in "password", :with => "bla"
    end
    And "I click login" do
      page.click_button "Login"
    end
    Then "I should see error" do
      page.should have_content("Invalid credentials")
    end
  end
end
And documentation output will be:
$ rspec -fs -c spec/acceptance/signup_feature_spec.rb
User signup
  Signing in with correct credentials
    When I go to sign in page
    And I fill in right email
    And I fill in right password
    And I click login" do
    Then I should see greeting
  User tries to sign in with incorrect password
    When I go to sign in page
    And I fill in right email
    And I fill in wrong password
    And I click login
    Then I should see error

Finished in 13.55 seconds
2 examples, 0 failures
Now it’s much better. Isn’t it? :)

RSpec Example Steps features

  • Does not hack RSpec Example. It only extends it.
  • Brings Given/When/Then/And/But steps support into rspec example block that allow you to better organize scenario
  • Adds shared_steps DSL
  • Adds ability to mark step as pending
  • Reports with color about passed/pending/failed step into documentation formating output

Installation

$ gem install rspec-example_steps

Usage

See README for more details.Just add to spec/spec_helper.rb
require 'rspec/example_steps'
And enjoy Given/When/Then step usage!

References