In the Building a real web application with Cucumber (Intermediate) recipe, we completed five simple user stories driven by Cucumber, and we learnt how to drive a Rails web application development using Cucumber in a BDD style.
Gherkin provides various kinds of expressive syntax. In this recipe, we are going to learn various kinds of skills and tips with Cucumber Gherkin DSL, and how to write readable, organized, and reusable Gherkin to help us solve real-world problems.
We will reuse the Rails application, cucumber_bdd_how_to
, which we created in the Writing your first Hello World feature (Simple) recipe, so please cd
into that directory to get prepared.
In the following sections, a number of useful Gherkin tips are introduced and covered exhaustively.
When we write Cucumber tests, we usually encounter this situation: a number of scenarios rely on the same step(s). For example, as a registered developer on GitHub, I can manage my profile, my repositories, or my SSH keys, then our feature can be written as follows:
Feature: Github account management In order to manage my profile, repositories and my SSH Keys As a registered developer I Should be able to log into system and manage my account Scenario: Change my avatar Given I logged into Github with account "Wayne Ye" When I click my avatar to go to the "Your Profile" page Then I can change my avatar with "Wayne.png" Scenario: View my repositories Given I logged into Github with account "Wayne Ye" When I click my avatar to go to the "Your Profile" page And I click on "Repositories" hyperlink Then I should be able to see all my repositories Scenario: Add a SSH KeyGiven I logged into Github with account "Wayne Ye" When I click my avatar to go to the "Your Profile" page And I click on "SSH Keys" hyperlink Then I add a new SSH key
You will notice that all the scenarios require you to log into GitHub, and in more complex cases the share steps can be more. This could be quite tedious, so in this situation we can use
Background
:Feature: Github account management In order to manage my profile, repositories and my SSH Keys As a registered developerI Should be able to log into system and manage my account Background: Given I logged into Github with account "Wayne Ye" When I click my avatar goto the "Your Profile" page Scenario: Change my avatar Then I can change my avatar with "Wayne.png" Scenario: View my repositories And I click on "Repositories" hyperlink Then I should be able to see all my repositories Scenario: Add a SSH Key And I click on "SSH Keys" hyperlink Then I add a new SSH key
Whenever possible, use Gherkin Background
to centralize the scenario shared steps, be DRY (don't repeat yourself), and get better maintainability!
Sometimes we need to describe the data in steps. When the data is in pieces, our step could be verbose, for example, a user registration feature:
Feature: User registration In order to shop in ABC online shop As a userI should be able to register an account through registration page Scenario: register with valid information When I am on the registration page of ABC online shop And I fill the "Full Name" form with "Wayne Ye" And I fill the "Address" form with "123 Main Street" And I fill the "Email" form with "[email protected]" And I fill the "Password" form with "asdf" Then I should be redirected to registration success page
Luckily, Gherkin provides a data table to deal with these kinds of scenarios. The data table gives you a way to extend a Gherkin step beyond a single line to include a larger piece of data. The preceding example can be easily and gracefully described as follows:
Feature: User registration In order to shop in ABC online shop As a userI should be able to register an account through registration page Scenario: register with valid information When I am on the registration page of ABC online shop And I fill the form with the following value | Full Name| Address | Email | Password | | Wayne Ye | No 12 Pt Street| [email protected] |asdf| Then I should be redirected to registration success page
Much greater readability and maintainability! The preceding data table contains column headers to specify each field, and sometimes it can also be used without headers, for example, when it represents a list of data.
Scenario: instance messenger online When I log into ABC social website And I open up the IM tab Then I should see my online friends | Mark | | Sean | | Shelly | | Wendy |
Consider a number of scenarios. They require similar steps as a part of them; the difference is different input values. Typically, a registration validation scenario is a good example:
Background: When I am on the registration page Scenario: user registration When I fill "email" field with "" And I press "Register" button Then I should see error message "Email cannot be blank" When I fill "email" field with "wayne" And I press "Register" button Then I should see error message "Please input valid Email address" And I fill "password" field with "" And I press "Register" button Then I should see error message "Password cannot be blank" And I fill "password" field with "asdf" And I press "Register" button Then I should see error message "Password is too short"
Ouch! A registration in the real world is definitely more complex than this one! The preceding scenario looks so ugly; the right way is to adopt Gherkin's
Scenario Outline
:Scenario Outline: user registration When I fill "<field_name>" field with "<value>" And I press "Register" button Then I should see error message "<error_message>" Examples: |field_name| value | error_message | | Email | | Email cannot be blank | | Email | wayne | Please input valid Email address | | Email |[email protected]| Email has already been taken | | Password | | Password cannot be blank | | Password | asdf | Password is too short |
By utilizing Scenario Outline, the feature looks clean and easy, and it refrains from writing duplicate steps. Additionally, we can quickly find out missed cases because of its tidy text. For example, in the previous case, we can easily point out that we lack the maximum length check for both the e-mail and password.
Sometimes we have a string with a new line in the step; in this scenario we can use Gherkin Dot Strings to represent the long string. Typically a step with account activation e-mail content is as follows:
Scenario: User registration When the user clicks "Register" Then an Email should be sent out with content: """ Dear customer, Thank you for registering at ABC website! Please click the following link to activate your account! http://foo-web.com/user/wayne/activation """
In the implementation step, the string content will be passed in as
Cucumber::Ast::Docstring
:Then /^an Email should be sent out with content:$/ do |string|p string.class # Cucumber::Ast::DocString p string # "Dear customer,\nThank you for registering at ABC website!\nPlease click the following link to activate your account!\nhttp://foo-web.com/user/wayne/activation" end
There are two extra tips for organizing Cucumber features and writing better features.
Gherkin allows you to add meaningful tags, for example:
Mark important, minimum marketable features as
@important
or@MMV
Mark features that require logging into the system as
@require_login
Mark work in progress features as
@todo
or@wip
Tag(s) can be applied to features or scenarios; scenarios, scenario outlines, or examples will inherit any tags that exist on the containing feature.
Tag(s) are pretty useful and provide many benefits. For example, they can help in organizing and filtering features:
We can specify Cucumber to run all features marked as
@mandatory
:$ cucumber --tags @mandatory
We can specify Cucumber to run all features except
@todo
ones:$ cucumber --tags ~@todo
We can specify Cucumber to run all
@finished
and@integration
features:$ cucumber --tags @finished --tags @integration
We can specify Cucumber to run features with a maximum number limit. The preceding command demonstrates both the ORing tags and the tag limits:
$ cucumber --tags @dev:4,@qa:6
Cucumber will fail if there are more than four @dev
features or more than six @qa
features, even if all the features passed. This tip will be pretty useful in the Kanban development methodology (a scheduling system invented by Toyota for the Lean and Just-in-Time productions) because we want to limit the number of Working in Progress features.
Apply tag logic by using hooks. We can add a block of Ruby code before/around/after a specific tag. This is massively useful, for example, because we can implement login logic before all features/scenarios marked with a @require_login
tag:
Before('@require_login') do # Put login logic here end After('@require_login') do # Perform logout logic end
When we run the @require_login
tag, the login logic hook will be executed automatically. Here it just demonstrates the tagged hooks. There are also scenario hooks, step hooks, and global hooks.
Do you remember in the beginning of the previous recipe, we wrote a Gherkin feature for the first story:
Feature: Write blog As a blog owner I can write new blog post Scenario: Write blog Given I am on the blog homepage When I click "New Post" link And I fill "My first blog" as Title And I fill "Test content" as content And I click "Post" button Then I should see the blog I just posted
Every step in this feature is granular; it describes each action used. Steps like these are called imperative steps (or communicative); as a comparison, another pattern of writing Gherkin is declarative (or informative), which suggests describing the user story over recording the user's actions.
For example, writing the previous feature in the declarative style will be as follows:
Feature: Write blog As a blog owner I can write new blog post Scenario: Write blog Given I am on the blog homepage When I write a new blog post Then I should see the blog I just posted
A declarative step usually hides more details and provides better readability for business people. Many people prefer declarative steps over imperative steps since imperative steps could be brittle because they are usually tightly coupled with UI or a serial of business logic, whereas both of them could be changed as per the requirement.
Note
In the older version of Cucumber, there used to be a web_steps.rb
generated under the step_definitions
directory every time Cucumber got installed. However, to avoid people writing imperative steps, Aslak Hellesøy (the co-author of Cucumber) removed it. His original statement is as follows:
"The reason behind this is that the steps defined in web_steps.rb leads people to write scenarios of a very imperative nature that are hard to read and hard to maintain. Cucumber scenarios should not be a series of steps that describe what a user clicks. Instead, they should express what a user does."
However, an imperative step is not always bad. Using imperative steps in some cases is more appropriate and natural than declarative steps. It is usually simple to read and understand, so the suggestion here is trying to write declarative steps but using imperative steps whenever the need arises.