Cucumber Best Practices

vladlion77 edited this page Jan 8, 2015 · 4 revisions

Features

Feature Files Should Actually be Features, Not Entire Portions of an App

Group your feature files by a business object, then action, and a context (if applicable). You can put all feature files in the same directory, e.g.:

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 a business object as well (bank_account_steps.rb, user_steps.rb…). Keep the file organized by grouping the steps with Given / When / Then.

Another option is to create a sub folder for every business object.

RULES:

  • Group feature files by an application domain, not by an actor.
  • 1 Feature = 1 File

Scenarios

Adding the details to a lower layer of implementation makes it is easier to write declarative scenarios, i.e.

Given the user navigates to the home page
And enters "username" in the "username" field
And enters "password" in the "password" field
When the user clicks on the login button
Then the user is logged in

could be written as

Given the user is logged in
And is on the home page

Using declarative style helps both the person writing and maintaining the tests in the long run as well as the product owner to see the forest from the trees, i.e. the acceptance criteria of the feature instead of a huge number of specific actions the user needs to take to accomplish the task.

There shouldn’t be any sort of coupling between scenarios. The main source of such coupling is the state that persists between scenarios. This can be done accidentally, or even worse, intentionally. For example, one scenario could step through adding a record to a database, while the subsequent scenarios depend on the existence of that record.

This may work, but will create a problem if the order in which scenarios run changes, or they are run in parallel. Scenarios need to be completely independent.

Each time a scenario runs, it should run the same, giving identical results. The purpose of a scenario is to describe how your system works. If you don’t have confidence that this is always the case, then it does not do its job. If you have non-deterministic scenarios, find out why and fix them.

If you use the same steps at the beginning of all scenarios for a feature, put them into the feature’s Background. Background steps are run before each scenario. Avoid putting too many steps there since your scenarios might become difficult to understand.

RULES:

  • Use a declarative style.
  • Make scenarios independent and deterministic.
  • Use backgrounds wisely.

Steps

Steps must be clear and understandable (simply by reading the description).

Each step has to perform one action. Avoid using “and” in the step patterns, e.g.:

Given A and B

Instead, use two steps:

Given A
And B

The matched phrases must not necessarily be enclosed in the double quotes. Often quotes are a good idea as they make it visually clear which phrases will be passed to the step definition, e.g.:

Given I have "potatoes" in my cart

That’s reasonable. The quotes are meant to highlight the parts that are changed without hurting readability. But sometimes, quotes are just a poor style:

Given I have "7" items in my cart

It should be pretty obvious that the number is a variable. The quotes add nothing but the noise. A good example of a step is:

Then /^I have (\d+) items? in my cart$/ do |item_count|
  item_count.to_i.times { ... }
end	

RULES:

  • The step description must contain neither regexen, CSS or XPath selectors, nor any kind of code or data structure.
  • Avoid using conjunctive steps.
  • Anything that is literally used must be in double quotes, otherwise, it should be specified within the sentence.

Step Definitions

Add a ? immediately following the pluralized word:

Then /^the users? should receive an email$/ do
# ...
end

You can create non-capturing groups by adding a “?:” to the beginning of an otherwise normal group (e.g. (?:some text) rather than (some text)). This is treated exactly as a normal group except that the result is not captured and thus not passed as an argument to your step definition. This might be useful in conjunction with the alternation:

And /^once the files? (?:have|has) finished processing$/ do
# ...
end

Or another pattern I use regularly:

When /^(?:I|they) create a profile$/ do
# ...
end

That’s a bad tradeoff if you remove a small amount of duplication while adding a lot of complexity.

RULES:

  • Use flexible pluralization.
  • Use non-capturing groups to ensure steps are read naturally.
  • Simplify step definitions.