Skip to content
Apr 24 / John Wyles

Turning a Pickcle into a Cucumber

I had seen Cucumber before but never really had any time to dabble with it and now that I have I cannot put it down! After setting up a skeleton Ruby on Rails application using Cucumber, Capybara, Devise, and FactorGirl (along with Haml, Compass, and MongoMapper) I decided to circle back and write some Cucumber features to cover the basic user interactions: sign up, confirmation, login, logout, lost password, etc. While it took me much longer than I expected my journey was very fruitful; it turns out some of the Features and their subsequent Scenarios were entirely covered by Cucumber and Capybara without me needing to write one bit of “glue code” to make them work (Cucumber calls these “step definitions”). Most of the time I spent was refactoring out step definitions over and over until I found I did not need a single one! Here is the final product from the “Signing Up” feature:

# features/authentication/signup.feature

Feature: Signing Up
  In order to sign up for an account
  As a guest
  I need to be able to register

  Scenario: Registration
    Given I am on the sign up page
    When I fill in "user_email" with "test@example.com"
    And I fill in "user_password" with "test1234"
    And I fill in "user_password_confirmation" with "test1234"
    And I press "Sign Up"
    Then I should see "You will receive an email with instructions"

Even after all of this I cannot be sure that I have completely covered the test but I will note that it does not require a single step definition outside of those that are provided by Capybara in the web_steps.rb convenient step definitions. Here are the other Features, Backgrounds, Scenarios, and Scenario Outlines that I used to cover the authentication class of features:

The confirmation feature:

# features/authentication/confirmation.feature

Feature:
  In order activate my account
  As a user
  I want to be able to confirm

  Background:
    Given I am pending confirmation

  Scenario: Confirmation
    Given I received the email
    When I confirm the account
    Then I should find that I am confirmed

The session feature:

# features/authentication/session.feature

Feature: Session handling
  In order to use the site
  As a registered user
  I need to be able to login and logout

  Background:
    Given that a confirmed user exists

  Scenario Outline: Logging in
    Given I am on the login page
    When I fill in "user_email" with "<email>"
    And I fill in "user_password" with "<password>"
    And I press "Sign in"
    Then I should <action>
    Examples:
      |         email       |  password   |              action             |
      | minimal@example.com |  test1234   | see "Signed in successfully"    |
      | bad@example.com     |  password   | see "Invalid email or password" |

  Scenario: Logging out
    Given I am logged in
    When I go to the sign out link
    Then I should see "Signed out successfully"

The forgotpassword feature:

# features/authentication/forgotpassword.feature

Feature: Forgot password
  In order to login
  As a user
  When I have forgotten my password
  I should be able to reset it

  Background:
    Given that a confirmed user exists

  Scenario Outline: Reset password request
    Given I am on the forgotten password page
    When I fill in "user_email" with "<email>"
    And I press "Send me reset password instructions"
    Then I should <action>
    Examples:
      |         email       |                                       action                                       |
      | minimal@example.com | see "You will receive an email with instructions about how to reset your password" |
      | bad@example.com     | see "Email not found"                                                              |

  Scenario: Reset password confirmation
    Given that I have reset my password
    When I follow the reset password link in my email
    Then I expect to be able to reset my password

and all of the step definitions:

# features/step_definitions/authentication_steps.rb

# Confrimation
Given /^I received the email$/ do
  @user.confirmed?.should be_false
end

When /^I confirm the account$/ do
  visit user_confirmation_path(:confirmation_token => @user.confirmation_token)
  page.should have_content("Your account was successfully confirmed")
  @user.confirm!
end

Then /^I should find that I am confirmed$/ do
  @user.confirmed?.should be_true
end

# Forgottenpassword
Given /^that I have a confirmed account$/ do
  @user.confirmed?
end

Given /^that I have reset my password$/ do
  @user.send_reset_password_instructions
end

When /^I follow the reset password link in my email$/ do
  visit edit_user_password_path(:reset_password_token => @user.reset_password_token)
  page.should have_content("Change your password")
end

Then /^I expect to be able to reset my password$/ do
  visit edit_user_password_path(:reset_password_token => @user.reset_password_token)
  fill_in 'user_password', :with => 'test1234'
  fill_in 'user_password_confirmation', :with => 'test1234'
  click_button('Change my password')
  page.should have_content('Your password was changed successfully')
end

# Session
Given /^I am logged in$/ do
  visit path_to('the login page')
  fill_in('user_email', :with => @user.email)
  fill_in('user_password', :with => @user.password)
  click_button('Sign in')
  if defined?(Spec::Rails::Matchers)
    page.should have_content('Signed in successfully')
  else
    assert page.has_content?('Signed in successfully')
  end
end

To get the minimal user, referenced in a few of the step definitions, I created a Factory:

# spec/factories/user.rb

Factory.define :minimal_user, :class => User do |u|
  u.email 'minimal@example.com'
  u.password 'test1234'
  u.password_confirmation 'test1234'
end

And added the FactoryGirl gem to the Cucumber environment:

# config/environments/cucumber.rb

config.gem "factory_girl"

As you can see there is a lot here and a lot of it probably duplicates the test coverage found in the Devise gem. However, developing these Features, Scenarios, and Step Definitions has been great practice for me and warmed me up very gently to how fun Behavior Driven Design (BDD) can be.

3 Comments

leave a comment
  1. Darren Shafae / Jul 7 2010

    Hello,

    I am attempting to familiarize myself with cucumber and I thought it would be a great idea to test the devise plug-in. I stumbled across your blog and I am using a very similar methodology. However, I am getting stuck on the testing of log in. It seems that I can not successfully log in.

    I was wondering if you have a similar problem when you followed:
    Scenario Outline: Logging in
    12 Given I am on the login page
    13 When I fill in “user_email” with “”
    14 And I fill in “user_password” with “”
    15 And I press “Sign in”
    16 Then I should
    17 Examples:
    18 | email | password | action |
    19 | minimal@example.com | test1234 | see “Signed in successfully” |
    20 | bad@example.com | password | see “Invalid email or password” |

    I know I am passing the correct email/password combination, but continually receive, “Invalid email or password.” Any help would be appreciated.

    -Darren

    • docgecko / Jul 24 2010

      Hi Darren,

      lines 13, 14 & 15 should be:

      13 When I fill in “user_email” with “”
      14 And I fill in “user_password” with “”
      15 Then I should

      Looks like you were just missing the link to the Examples.

  2. docgecko / Jul 24 2010

    Great, great article. I have been looking to do this myself recently, but so good to see someone has already had a go.

    Anyway, just wanted your advise, as I am quite new to cucumber. I ran through this and am left with 2 undefined steps:

    Given /^I am pending confirmation$/ do
    pending # express the regexp above with the code you wish you had
    end

    Given /^that a confirmed user exists$/ do
    pending # express the regexp above with the code you wish you had
    end

    Which are coming from the Backgrounds of confirmation.feature and forgotpassword.feature.

    I have been trying to fill in the “pending” text, but without joy. Could you help me to resolve this?

    Many thanks,

    D.

Leave a Comment