(My) Cucumber Best Practices and Tips

After (My) RSpec best practices and tips, I’m happy to share my Cucumber best practices and tips!

This article will help you organize, clarify and reduce the size of your cucumber scenarios.

1. Organize your garden

Keep your feature files organized by grouping them by business object, then action and context if any. I put all the feature files in the same directory. For instance:

1
2
3
4
5
bank_account_add.feature
bank_account_delete.feature
user_signup.feature
user_signup_when_invited.feature
user_login.feature

The steps specific to the application should be organized by business object as well (bank_account_steps.rb, user_steps.rb…). Keep the file organized grouping the steps by Given / When / Then.

Do not overload the files generated by Cucumber like step_definitions/web_steps.rb and support/env.rb with your own steps, helpers or setup code. These files are likely to get overwritten when you update Cucumber so store your stuff in your own files.

2. Custom steps make your scenario DRY and accessible

Scenarios should have the same lifecyle as your code: Red, Green, Refactor to make them DRY and easy to read.

Group multiple steps together. For instance:

1
2
3
4
5
6
  Given I follow "Send money"
  When I fill in "Email" with "mukmuk@example.com"
  And I fill in "Amount" with "10"
  And I select "Bank account" from "Source"
  And I press "Send"
  Then I should see "You've sent $10 to mukmuk@example.com"

… could be refactored to:

1
  Given I send "$10" to "mukmuk@example.com" from my "Bank account"

This step definition is the following:

1
2
3
4
5
6
7
8
  Given %{I send "$amount" to "$email" from my "$source"} do |amount, email, source|
    Given %{I follow "Send money"}
    When %{I fill in "Email" with "#{email}"}
    And %{I fill in "Amount" with "#{amount.delete('$')}"}
    And %{I select "#{source}" from "Source"}
    And %{I press "Send"}
    Then %{I should see "You've sent $#{amount} to #{email}"}
  end

This step can then be easily reused in other scenario keeping your features DRY. It also decouples the scenario from the UI so that you won’t have to change dozens of feature files when the UX guru changes translations or user flows.

3. Background: setup the DRY way

Make the feature focus on one business object/action/context and the background will get longer than the scenarios.

1
2
3
4
5
6
7
8
9
10
11
12
13
  Feature: A user can cancel a transaction unless it's claimed by the recipient

    Background:
      Given I am logged in
      And I send "$10" to "mukmuk@example.com" from my "Bank account"

    Scenario: I can cancel as long as the payment is not claimed
      When I cancel my latest transaction
      Then I should see a cancellation confirmation

    Scenario: I can't cancel once the payment is claimed
      Given "Mukmuk" claimed the latest transaction
      Then I can't cancel my latest transaction

4. Scenario outlines: scenario with variables!

A scenario outline contains variables allowing you to test multiple context using a truth table. For instance I use them to make sure that validation errors are displayed properly:

1
2
3
4
5
6
7
8
9
10
11
  Scenario Outline: Add invalid bank account displays inline errors
    Given I follow "Add Bank Account"
    When I fill in "<field>" with "<value>"
    And I press "Add Bank Account"
    And I should see the inline error "<error>" for "<field>"

    Examples:
      | field   | value         | error                    |
      | Account |               | Can't be blank           |
      | Account | Sixty five    | Should be 1 to 12 digits |
      | Account | 1234567890123 | Should be 1 to 12 digits |

5. Multi-line step arguments: give your step a table for lunch!

A step can take a multi-line table as an argument. This is a great way to load up a bunch of data or to test the rendering of lists and tables. For instance:

1
2
3
4
5
6
  Given I sent "$25" to "mukmuk@example.com" from my "Bank account"
  Then I should see the following transaction history:
    | create     | complete    |
    | deposit    | in_progress |
    | transfer   | pending     |
    | withdrawal | pending     |

The step definition looks like the following:

1
2
3
4
5
  Then "I should see the following transaction history:" do |table|
    table.raw.each do |event, state|
      page.should have_css("tr.#{event}.#{state}")
    end
  end

I hope that these tips will help you growing healthy cucumber features!

Want more? Check out the Cucumber wiki, 15 Expert Tips for Using Cucumber by Dave Astels, You’re Cuking It Wrong by Elabs and You’re almost cuking it… by Antony Marcano. You could also like (My) RSpec best practices and tips. :)

Happy BDD!

Thanks to Jean-Michel Garnier for reviewing this article.

Comments