Top 5 Cucumber Best Practices for Selenium Automation
Do you know Cucumber is a great tool used to run acceptance tests using the plain-text functional descriptions with Gherkin? Behavior Driven Development strategy or BDD, as it is popularly known, is implemented using the Cucumber tool.
The best part about using the Cucumber BDD framework are:
- Tests are first documented before being implemented.
- Tests are easy to understand for a user who doesn't even know the functionality.
- It efficiently combines the automated tests having living documentation and specifications that can be executed.
Can't wait to get started with Cucumber? To help you out, we will be diving into some of the best Cucumber practices that will enable you to write better scenarios using the Gherkin language. You will also get a clearer picture of the Behavior Driven Development concepts with these practices.
Basics of Cucumber BDD Framework
Before we jump dive into Cucumber best practices, there are a few things you need to understand about the Cucumber BDD framework. First, to work with Cucumber for Selenium automation testing, you would need three types of files as described below:
- Feature File: It serves as an entry point to the Cucumber tests. It is the file where your test scenarios are written in the Gherkin language. A Feature file may contain single or multiple test scenarios. The Feature file is used as a live document and ends with a .feature extension.
- Step Definition: It contains the piece of code, in your chosen programming language, with some annotations attached to it. On seeing a Gherkin Step, Cucumber executes the code which is contained within the Step. The annotation has a pattern that links the Step Definition to the matching steps defined in the Feature File.
- Others: We may need other files to execute tests at different levels. For example, we are testing the Web UI; then, we will be using a tool like Selenium, which might use a framework of its own like the Page Object Model. Since, in this post, our primary focus is Cucumber best practices, let us leave the other files' details for some other time.
In the next section of this blog, we will understand feature files in detail and how we can use them efficiently. These are some of the essential practices you should implement for successfully using Cucumber and Selenium. As already stated, we will use Gherkin to write the scenarios in the Cucumber BDD framework. Let us now understand in detail some Cucumber best practices.
1. Creating a Feature File
We will start by creating a file in our project structure that will consist of the steps to mimic a certain functionality. Since, in this post, we will understand Cucumber's best practices, we will only focus on how we can write our features file to model our test scenarios. We will see the practical implementation later. As an example, let us take the Login functionality using Gherkin.
Use Case: Model the behavior of logging into an application with valid credentials:
- Create a file with .feature extension inside the project folder. For example, let us name it "Login.feature".
- Inside the file, we will give a title depicting the functionality. So in our example, it can be something like "Feature: Login Action."
- We will now start writing our scenarios in the feature file. The general syntax for writing a scenario in a feature file is:
As [a user]
I want to [perform some action]
for [ achieving a result]
So using the above two points, let us start with writing a Feature:
Feature: Login Action
Scenario: As an existing user, I want to log in successfully.
With this, you need to make a note of the important points listed below:
- First, it is advised that you make your feature file independent from other functionalities. This means trying to make each feature specific to a single functionality.
- You can make your feature file understandable by using the same language as the requirement is specified, i.e., always try to describe the actions as they would have been done by the client.
Next, in the feature file, you will be writing the Scenarios. Scenarios are simply the behavior of a functionality. While testing, we might have to write multiple scenarios to cover the test scope. To write a scenario, we use Keywords defined by Gherkin. The primary keywords used in Gherkin sentences are:
1. Given: Defines the pre-condition of the test.
2. When: Defines the user action that will be performed.
3. Then: Defines the post-condition or the outcome of the test.
4. But: Used to add negative conditions to the test.
5. And: Used to add a condition(s) to the test.
Note that you only need to state what you want to do in the feature file and not how you want to do it. The how part will be taken care of in the Step Definition file, which we will see later in this article.
See below an example of a poorly written scenario:
Scenario: As an existing user, I want to login successfully
Given the user is on the Home page
When the user navigates to the Login page
And the user can see the login form
And the user enters username and password
And the user is able to click on the Submit button
Then the user is logged in successfully
And the successful login message is displayed
There is no point in writing such lengthy scenarios with unwanted details as it makes it difficult to read and maintain. A better way to write the same scenario with fewer lines is as follows:
Scenario: As an existing user, I want to login successfully.
Given the user is on the Home page
When the user navigates to the Login page
And the user enters username and password
Then the successful login message is displayed
Did you see how with fewer sentences, we can depict the same scenario by including only the necessary details and ignoring beating around the bush? :slightly_smiling_face:
Below are a few points that you need to keep in mind while writing scenarios in Gherkin:
- Always remember that the order of your statements must follow Given-When-Then. Since 'Given' implies a pre-condition, 'When' refers to an action, and 'Then' refers to a post-condition for the action, it will be unclear to write 'Then' before 'When.'
- Always remember that Given-Then-When should occur only once per scenario. You can extend any sentence by using 'And.' This is because every scenario depicts an individual functionality. If we will include multiple Then-When, there would be no point in being a single functionality.
- Make sure that your sentences are consistent when talking about perspective. This means if the scenario description is described in first person, then the sentences should also be in first person to maintain homogeneity.
- Try to write the minimum steps in a scenario. It helps in making the scenario understandable and clear.
- Try writing brief sentences which are explanatory.
- Try to make your scenarios independent. If the scenarios are interlinked, it may generate errors, for instance, in the case of parallel test execution.
2. Separating Feature Files
When testing with live applications, you might have to create multiple feature files. It becomes crucial to bifurcate the feature into different files. You can organize files so that all the features related to a specific functionality are grouped in a package or a directory. This is another one of the essential Cucumber best practices we recommend for seamless BDD implementation.
For example, consider an e-commerce application; you can organize the file such that, at the first level, you can have a package, say Orders, and in that, you can have multiple features like Pending Orders, Completed Orders, Wishlist, etc. Doing so will make your project organized, and it will be easy for you to locate the tests as per the functionality.
3. Using The Correct Perspective
At times it becomes very confusing as to what perspective should you write your scenarios in; the first person or third person. The official Cucumber BDD framework documentation uses both points of view. Below are the arguments for both the point of view:
First Person
BDD was created by Dan North, who, in his article "Introducing BDD," recommends the use of the first person. Using the first person is rational since it depicts keeping yourself in place of the person actually performing the action.
Third Person
The people who prefer the third-person point of view state that using the first-person can confuse the reader. It does not clarify who is performing the action, i.e., an individual user, an admin, or some user with a particular set of roles. It is argued that third-person usage shows the information formally and minimizes the risk of making any false assumptions about who is actually involved in performing/testing a scenario.
So, all in all, there is no mandate on using any one point of view; the one practice that you have to remember is to maintain consistency. The description should resonate with the test steps and be from a single perspective.
4. Additional Keywords Used in Gherkin
Apart from the commonly used keywords discussed above, there are a few more that are used in Gherkin. If you want to implement the Cucumber best practices, this is an important one to start practicing.
Background
Background simplifies adding the same steps to multiple scenarios in a given feature. This means if some common steps have to be executed for all the scenarios in a feature, you can write them under the Background keyword.
For example, to order a product from an e-commerce website, you will have to do the following steps:
- Open the website.
- Click on the Login link.
- Enter the username and password.
- Click on the Submit button.
Once you have completed the above steps, you can search for the product, add that product to your cart, and proceed with the checkout and payment. Since the above steps would be common for many functionalities in a feature, we can include them in the Background.
Feature: Add To Cart
Background:
Given the user is on the Home page
And the user navigates to the Login page
And the user enters username and password
Then the successful login message is displayed
Always try to keep the background as short as possible since it will be difficult to understand the following scenario if it is kept lengthy. The key with the Cucumber Feature file is the shorter, the better.
Scenario Outline
A Scenario outline is similar to the test data corresponding to a test scenario. It is no compulsion to write a scenario with a scenario outline, but you can write it if needed.
Scenario outline: Order with different quantities
Given: User searches for HP Pen Drive
When: Add the first result on the page with quantity <qty>
Then: Cart should display <qty> pen drive
Examples:
|qty|
|1|
|5|
|24|
Doc Strings
If the information in a scenario does not fit in a single line, you can use DocString. It follows a step and is enclosed within three double quotes. Though often overlooked, it is one of the most crucial Cucumber best practices to follow.
Scenario: Login with a valid-user
Given the user is on the Home page
And the user navigates to the Login page
And the user enters username and password
Then the successful login message is displayed with text:
“You have successfully logged into your account! There are multiple discount offers
waiting for you!!”
Data Table
The Data Table is quite similar to Scenario Outline. The main difference between the two is that the Scenario outline injects the data at the scenario level, while the data table is used to inject data at the step level.
- Data tables serve to input data in a single step.
- It is not necessary to define the head of a data table, but it is advised to maintain a reference to data for easy understanding.
Scenario: Login with a valid-user
Given the user is on the Home page
And the user navigates to the Login page
And the user enters <username> and <password>
| username | password |
| test1 | password1 |
As shown in the example above, you can use a data table in single steps with different data that you may need to inject.
Languages
Cucumber is not limited to writing the scenarios in English. Similar to the conventions followed in English, you can write the scenarios in multiple human languages. The official Cucumber documentation has all the information about using the Language feature and the dialect code of various languages.
For example, to use French as the language to write your scenarios, you can use the # language as a header in the functionality like below:
# language : fr
(Note: fr is the dialect code for French)
Tags
There may be cases when you need not execute all the scenarios of the test. In such cases, you can group specific scenarios and execute them independently by using Tags. Tags are simply the annotations used to group scenarios and features. They are marked with @ followed by some notable text.
Examples:
@SmokeTest @RegressionTest
Scenario: ….
@End2End
Feature: ….
Note that the tags are inherited in the feature file by all the components, viz the scenario outline, scenario, etc. Similarly, if there is a tag on the Scenario Outline, the data examples will also inherit the tag.
The above examples can be configured for execution as shown below:
tags={“@End2End”}
First, all the scenarios of the feature under @End2End tag would be executed.tags={“@SmokeTest”}
All the scenarios under @SmokeTest would be executed.tags={“@SmokeTest , @RegressionTest”}
This type of definition denotes OR
condition hence, all the scenarios that are under @SmokeTest
tag or @RegressionTest
the tag would be executed.tags={“@SmokeTest” , “@RegressionTest”}
In such a definition, all the scenarios under the @SmokeTest AND @RegressionTest
will be executed.tags={~“@End2End”}
All the scenarios under @End2End
the tag will be ignored.tags={“@SmokeTest , ~@RegressionTest”}
All the scenarios under @SmokeTest
the tag will be executed, but the scenarios under the @RegressionTest tag would be ignored.
Similar to the examples above, you can make combinations of tags as per your requirement and execute the scenarios/features selectively.
5. Step Definition(Step Implementation)
So far, we have only understood what our scenarios would do as part of Cucumber best practices. But the next and vital step to automate using Cucumber Selenium is adding a Step Definition that would do the how part, i.e., how would the scenario execute? When Cucumber runs a step in the Scenario, it refers to a matching Step Definition for execution.
Implementation of steps can be done in Ruby, C++, Javascript, or any other language, but we will use Java as our example.
If you are using an IDE that already has Gherkin and Cucumber installed, you will see suggestions to create a new .java file or select one which has the steps implemented already. On selecting any of the options, a method will be created in the class. For instance, we are resting the step definition for the below step:
Given the user is on Home Page.
A method would be generated automatically, with annotation having the header text the same as that of the step description:
@Given(“^the user is on Home Page$”)
public void homePage() throws Throwable{
//Java code to check the above description
….
…..
}
To create step implementation of scenarios that get data from Scenario Outline or Data Tables, the data is included in the annotations as regular expressions, along with passing as a parameter to the method.
@When(“^Add the first result on the page with quantity \”([0-9]+)”\$”)
public void addQuantity(int qty) throws Throwable{
//Java code to pass qty in the qty field
…
...
}
And that is how you can implement the steps that you write in the Feature file using Gherkin. Always remember the below points while implementing step definitions-
- Try to create reusable step definitions.
- Reusable step definitions will make your tests maintainable, and in case of any change in the future, you will have to make minimum changes to your framework.
- You can use Parameterization in scenario outlines to reuse step definitions.
Wrapping Up
You are now familiar with some of the most important Cucumber best practices to follow with your BDD strategy or while implementing Cucumber and Selenium. To summarize this blog post, we would recommend you to:
- Try to write scenarios in the feature file in a way the user would describe them. This would help you create crisp and concise steps.
- Avoid coupled steps, i.e., always prefer creating one action per step. This would save you from unnecessary errors.
- Reuse step definitions as much as possible to improve code maintainability.
- Try to leverage the use of Background to minimize unnecessary addition of the same steps in different scenarios.
Happy testing!