Cucumber – All you need to know

Cucumber is a collaboration tool that supports BDD (Behavior Driven Development). Cucumber has the ability to read executable specifications written in plain text and its primary focus is to validate whether the software conforms to the specifications or not.

What is BDD?

BDD or Behavior-Driven Development is a software development methodology that aims to narrow the communication gaps between the key stakeholders (e.g. Product owners, Business Analysts, Users, Domain Experts, Developers, UX Designers, Testers, Operation engineers and other team members) while developing a software and focuses on better understanding of the customer requirement and continuous communication with real world examples.

BDD is a process of approaching the design, forcing us to think about the desired outcome and API before coding starts.

Cucumber in BDD

One thing to keep in mind is that Cucumber is primarily not a testing tool. It is a tool for the overall development approach of a software. Automated Tests using Cucumber is one of the many things that Cucumber facilitates.

For automated tests, in order to understand the scenarios, Cucumber must follow some basic syntax rules called Gherkin, which makes plain text structured enough for Cucumber to understand. The Gherkin documents are stored in .feature text files. Example of a cucumber test scenario in Gherkin is as follows:

Scenario: Webpage Subscribe button Validation for unsubscribed customers
	Given the user opens the webpage
	When the user is an unsubscribed customer
	Then the user should be able to view the enabled subscribe button

More about Gherkin and Features later in the tutorial.

Cucumber Compatibility With Other Automation Tools

Cucumber is a not a Browser/API automation tool, but it works well with the following Browser automation tools:

  • Selenium WebDriver
  • Serenity BDD
  • Watir
  • Capybara

And with the following API automation tools:

  • RestAssured
  • Karate DSL
  • RestClient
  • HTTPParty
  • Net::HTTP
  • Faraday
  • RubyMine
  • Curb
  • Typhoeus

INTRODUCTION

The main features of Cucumber is Gherkin and Step Definitions.

GHERKIN

Gherkin is a simple set of grammar rules that makes plain text structured enough for Cucumber to understand. Gherkin serves multiple purposes like below:

  • Documents the system’s actual behavior
  • Unambiguous Executable specification understandable by everyone
  • Automated Testing

Gherkin exists in different spoken languages so that people can use their own language. The documents are stored in .feature files (e.g. LoginTest.feature).

If the team is practicing TDD (Test Driven Development), it is best if developers write the feature files. If Cucumber is used only for running automated tests, both developers and testers can contribute in writing the feature files.

Gherkin uses special keywords to give structure and meaning to the Feature files. The Gherkin lines start with one of the following primary keywords:

  • Feature
  • Scenario
  • Given, When, Then, And, But (for the steps)
  • Background
  • Scenario Outline
  • Examples

The secondary keywords are:

  • “”” (Doc Strings)
  • | (Data Tables)
  • @ (Tags)
  • # (Comments)

Sample Gherkin file:

# This line is a comment
Feature: Google Launch and Search

Scenario: Test to validate successful Google launch and search from Chrome browser
  Given user opens the "Chrome" browser in desktop
  When user opens the URL "https://www.google.com"
  Then user is able to successfully search for the item "Testing"
  And user is able to view the "Testing" related URLs

Let’s talk about each Keyword in detail:

Feature
The purpose of the Feature keyword is to provide a high level description of a software feature and to group related scenarios. Feature should be the first primary keyword in a Gherkin document, followed by a “:” and then a short text describing the feature. Free-form text can be written below to add some more description to the feature (which are ignored at runtime but available for reporting).

Feature: Google Launch and Search
about Testing

Scenario
The Scenario keyword describes a definitive example that illustrates a business rule. Each scenario consists of a list of steps. You can write as many steps under a scenario as you like, but a lot of steps usually mean that the scenario is actually losing its specification power.

Steps
Each scenario consists of steps arranged in the following pattern:
Given steps (Describing an initial context or precondition)
When steps (Describing an action or an event)
Then steps (Describing an expected result or outcome) And/But step (Helps to divide a large Given/When/Then condition into smaller, understandable and brief steps)

Cucumber executes each step in a scenario one at a time in a sequential manner. While trying to execute a step, it searches for a matching Step Definition present in the code file of the programming language. You cannot have duplicate step definitions present in your code for a single step.

Background
In a Feature, you might find some initial steps getting repeated for all the scenarios. Those steps are not essential to describe the scenario, but they are the incidental details. In such cases they can be grouped in the Background section, placed after the Feature description and before the first Scenario. The Background section allows you to add some context to the scenarios. It can contain one or more Given steps. It runs before each scenario execution. Also, remember that you can have only one set of background steps per Feature. If you need different Background steps for different scenarios, you might need to include those steps inside the individual Scenarios itself or you might need to split them into different feature files. Do not use Background to set up complicated steps, keep the Background short and clear.

Background:
    Given a testing blogger named "Sam"
    And a blog named "Sam’s Testing Blog"

Scenario Outline and Examples
It can be used to run the same Scenario multiple times, with different combinations of values. We can collapse two or more similar, repetitive scenarios into a Scenario Outline. A Scenario Outline must contain an Examples section. Its steps are interpreted as a template which is never directly run. Instead, the Scenario Outline is run once for each row in the Examples section beneath it (not counting the first header row).

In Scenario Outline, the steps can use <> delimited parameters that reference headers in the example table. Cucumber will replace these parameters with values from the table before it tries to match the step against a Step Definition.

Given there are <start> cucumbers
  When I eat <eat> cucumbers
  Then I should have <left> cucumbers

  Examples:
    | start | eat | left |
    |    12 |   5 |    7 |
    |    20 |   5 |   15 |

Doc Strings
These are useful for passing a large text to Step Definition.
The text should be offset by delimiters “”” marks on lines of their own.

Given a blog post named "Random" with Markdown body
  """
  Some Title, Eh?
  ===============
  Here is the first paragraph of my blog post. Lorem ipsum dolor sit amet,
  consectetur adipiscing elit.
  """

Data Tables
They are useful for passing a list of values to a step definition. They will be passed to the Step Definition as the last argument.

Given the following users exist:
  | name     | email              |
  | Sam      | sam@test.com       | 
  | Richard  | richard@test.com   | 
  | Tom      | tom@test.com       |

STEP DEFINITIONS

The trailing portion (after the keyword) of each step is matched to a code block called Step Definition.

Step Definitions map or glue each Gherkin step to programming code to carry out the action that should be performed by the step. Here, the programming language that we will use is Java. For example, taking into consideration the feature that we wrote above, the step definitions are:

user opens the "Chrome" browser in desktop
user opens the URL https://www.google.com
user is able to successfully search for the item "Testing"
user is able to view the "Testing" related URLs

INSTALLATION

Cucumber is available for most programming languages. The official implementations which are maintained are as follows:

Cucumber – JVM (For Java)
Cucumber.js (For Node.js and browsers)
Cucumber.rb (For Ruby and Ruby on Rails)
Cucumber.cpp (For C++)
Android (For Java)
Kotlin (Cucumber JVM with Kotlin)
Behave (For Python)


Here, we will be installing and using Cucumber-JVM, since we will be using Java.

Cucumber-JVM is published in the Central Maven Repository. You can start working on Cucumber-JVM by either adding the following Maven or Gradle dependencies to your project or by directly downloading the jar files and adding them in your project configuration path.

Maven Dependency
Add the following Maven dependencies in your pom.xml file.

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-java</artifactId>
    <version>4.7.1</version>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>4.7.1</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>info.cukes</groupId>
    <artifactId>cucumber-jvm-deps</artifactId>
    <version>1.0.5</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-html</artifactId>
    <version>0.2.7</version>
</dependency>
<dependency>
    <groupId>net.masterthought</groupId>
    <artifactId>cucumber-reporting</artifactId>
    <version>4.8.0</version>
</dependency>

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-core</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>2.0.2-beta</version>
    <scope>test</scope>
</dependency>

Gradle Dependency
Add the following Gradle dependencies in your build.gradle file.

dependencies {
    testCompile 'io.cucumber:cucumber-java:4.7.1'
    testCompile 'io.cucumber:cucumber-junit:4.7.1'
    providedCompile group: 'info.cukes', name: 'cucumber-jvm-deps', version: '1.0.5'
    compile group: 'io.cucumber', name: 'cucumber-html', version: '0.2.7'
    compile group: 'net.masterthought', name: 'cucumber-reporting', version: '4.8.0'
    testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
    testCompile group: 'org.mockito', name: 'mockito-all', version: '2.0.2-beta'
}

repositories {
    mavenCentral()
}

Download as JARs
If you want to use the JAR files, download the following JARs from the Maven Repository and add them directly to your project configuration path:

  • cucumber-core-4.2.6.jar
  • cucumber-java-4.2.6.jar
  • cucumber-junit-4.2.6.jar
    (Remember, the above three jars should have the same version)
  • cucumber-jvm-deps-1.0.5.jar
  • cucumber-html-0.2.7.jar
  • cucumber-reporting-4.7.0.jar
  • gherkin-5.1.0.jar
  • hamcrest-core-1.3.jar
  • mockito-all-2.0.2-beta

Cucumber is a JUnit extension. It is launched by running JUnit from your build tool (e.g. Maven, Gradle) or from your IDE (e.g. Eclipse, Netbeans, IntelliJ etc.)

If you are using Eclipse, you would also like to install the Cucumber-Eclipse-Plugin. To install it, follow the below steps:

Open Eclipse IDE
Go to Help -> Eclipse Marketplace
Enter Cucumber in the Find search box and click on Search
Click on “Install” button for Cucumber Eclipse Plugin

Follow the steps and Cucumber-Eclipse-Plugin will get installed in your Eclipse. Restart Eclipse to view the changes.

Cucumber Installation from Command Line (for Maven Project)

Create a new Maven project by opening the command prompt and going to the directory where you want to create the project. Then run the following command:

mvn archetype:generate                      
   "-DarchetypeGroupId=io.cucumber"           
   "-DarchetypeArtifactId=cucumber-archetype" 
   "-DarchetypeVersion=4.2.6.1"               
   "-DgroupId=HelloCucumber"                  
   "-DartifactId=HelloCucumber"               
   "-Dpackage=helloCucumber"                  
   "-Dversion=1.0.0-SNAPSHOT"                 
   "-DinteractiveMode=false"

Your Maven project will get created. Then open Eclipse and import this Maven project (File -> Import -> Maven -> Existing Maven Projects -> Next -> Browse to the project path -> Finish)
A file RunCucumberTest.java will automatically get created inside the path “/HelloCucumber/src/test/java/helloCucumber/”. Add the below code in the file:

package helloCucumber;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(plugin = "pretty", features = "src/test/resources/hellocucumber")
public class RunCucumberTest {
}

To verify that the Cucumber installation is successful for the Maven project, run the below command from the command prompt (you should be inside the directory of the project “HelloCucumber”):
mvn clean test

You should be able to see “BUILD SUCCESS”. This means that Cucumber is successfully installed and you are ready to run your first scenario using Cucumber.

CREATING SCENARIOS

Scenarios are Executable Specifications. Before the actual development of a software gets started in BDD, there should be some concrete examples which should define what the software is supposed to do. These are Scenarios. As the production code emerges, scenarios become more of documentation and automated tests.

Scenarios are written inside “.feature” files. Let’s create a sample Feature file (FirstApplication.feature) and define some scenarios inside it:

Feature: Testing the FirstApplication

Scenario: FirstApplication opens successfully in Desktop Chrome
  Given user opens the Chrome browser in desktop
  When user enters the application URL and searches
  Then the application gets opened successfully.

Scenario: FirstApplication opens successfully in Mobile Web
  Given user opens the Chrome browser in mobile device
  When user enters the application URL and searches
  Then the application gets opened successfully.

At this moment, if we ask Cucumber to execute this file (either by using mvn test command from command line or by running as Junit from the Runner class), Cucumber will show 2 undefined Scenarios and 4 undefined Steps. It will also suggest some code snippets that we can add to define these steps.

@Given("^user opens the Chrome browser in desktop$")
public void user_opens_Chrome_browser_in_desktop() {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

@Given("^user opens the Chrome browser in mobile device$")
public void user_opens_Chrome_browser_in_mobile_device() {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}


@When("^user enters the application URL and searches$")
public void user_enters_the_application_URL_and_searches() {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}


@Then("^the application gets opened successfully$")
public void the_application_gets_opened_successfully() {
    // Write code here that turns the phrase above into concrete actions
    throw new PendingException();
}

You can copy each of these suggested code snippets and paste them inside the Step Definition file. Let’s fill the Step Definition file with some code:

package hellocucumber;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.When;
import cucumber.api.java.en.Then;


public class Stepdefs {

@Given("^user opens the Chrome browser in desktop$")
public void user_opens_Chrome_browser_in_desktop() {
    System.out.println(“Chrome Browser opened successfully in Desktop.”);
}

@Given("^user opens the Chrome browser in mobile device$")
public void user_opens_Chrome_browser_in_mobile_device() {
    System.out.println(“Chrome Browser opened successfully in Mobile.”);

}


@When("^user enters the application URL and searches$")
public void user_enters_the_application_URL_and_searches() {
    System.out.println(“Application URL search is successful.”);
}


@Then("^the application gets opened successfully$")
public void the_application_gets_opened_successfully() {
    System.out.println(“Application got opened successfully.”);
}

}

Run Cucumber again (either by using mvn test command from command line or by running as Junit from the Runner class). Your tests should now pass.

CONGRATULATIONS!! You have run your first successful tests using Cucumber.

Variables, Scenario Outline and Examples

As you can see in our FirstApplication.feature feature file, we are using different Given steps in the two scenarios, but the When and Then steps are same. We are basically replicating the When and Then steps in the scenarios. To facilitate reusability, let’s combine the 2 scenarios and create a single Scenario Outline which uses Variables and Examples.

Feature: Testing the FirstApplication

Scenario Outline: FirstApplication opens successfully
  Given user opens the Chrome browser in <device>
  When user enters the application URL and searches
  Then the application gets opened successfully.
Examples:
|device        |
|desktop       |
|mobile device |

To make this run, we also need to update the respective Given code snippet in the Step Definition file with the below code:

@Given("^user opens the Chrome browser in \"([^\"]*)\"$")
public void user_opens_Chrome_browser_in_desktop(String deviceType) {
    System.out.println(“Chrome Browser opened successfully in “+deviceType);
}

STEP DEFINITIONS

We saw earlier that each Cucumber Scenario in a Feature file consists of steps which are normal English sentences preceded by Gherkin Keywords Given, When, Then, And, But.

Step Definitions are the methods/ functions (sometimes containing Expressions) which connect to one or more Gherkin steps. When we write Gherkin steps, our program code need to understand and interpret the steps. Step Definitions help with that interpretation.

Let us consider the below Gherkin step that we presented earlier:

Scenario: FirstApplication opens successfully in Desktop Chrome
Given user opens the Chrome browser in desktop


The part “user opens the Chrome browser in desktop” of the step will match to the following Step Definition:

package hellocucumber;

import cucumber.api.java.en.Given;

public class StepDefinition {

@Given("^user opens the Chrome browser in desktop$")
public void user_opens_Chrome_browser_in_desktop() {
    System.out.println(“Chrome Browser opened successfully in Desktop.”);
}

Cucumber basically performs four steps one by one:

  • It matches a step against a step definition
  • It gathers any capture groups or variables or expressions
  • It passes them to the method of the step definition
  • It executes the steps

All step definitions are loaded and defined before Cucumber starts to run the Feature file. Once the execution begins, for each step, Cucumber will check for a corresponding step definition with a matching expression. Once found, it will pass all the capture groups and variables from the expressions as method arguments.

EXPRESSIONS

A Step Definition can have two types of Expressions:
1) Regular Expressions
2) Cucumber Expressions

The expressions are used to link a Gherkin step to a Step Definition. If a Regular Expression is used in the Step, then each matching capture group will be passed as a parameter to the method of the step definition. For Example, if we use a Regex \”([^\”]*)\” in the step definition.

@Given(“^user opens the Chrome browser in \”([^\”]*)\”$”) and use the step
Given user opens the Chrome browser in “desktop”, then the word “desktop” will be passed as a String parameter(i.e. deviceType) to the below Step Definition method:

public void user_opens_Chrome_browser_in(String deviceType)

This removes the need to write separate step definitions for similar scenario steps. We can use another step like below without creating a new step definition:
Given user opens the Chrome browser in “mobile device”

If you are using Cucumber Expressions, then the step and step definition would look like
Step: Given user opens the Chrome browser in “desktop”
Step Definition: @Given(“^user opens the Chrome browser in {string}$”)

Where, {string} is the parameter type. The other parameter types of Cucumber Expressions are {int}, {float}, {word}, {string} and {}.

Remember, the number of parameters in the Step Definition method has to match the number of expressions (Regular Expression/ Cucumber Expression) used in the step definition.

DATA TABLE

The simplest way to pass a list of parameters to a step definition is to use a data table. The values get passed in a List format automatically by Cucumber. Take an example of the below step:

Given the following cities
|Melbourne|
|New York   |
|London       |

The step definition would look like:

@Given(“^the following cities$”)
public void the_following_cities(List<String> cities){
}

HOOKS AND TAGS

Hooks are code blocks which run at specific moments in the Cucumber Execution Life Cycle. Typically these are used to perform some initial setup and final tear down activities. To get more control of the Cucumber hooks, we can use Tagged Hooks. The hooks can be declared in any of the classes. In Cucumber, there is no concept of Global Hooks and hooks that can run only once based on some condition. Let’s discuss more about the hooks. Following are the Cucumber hooks:

  • Before
  • After
  • BeforeStep
  • AfterStep

Before
The Before hook runs before the first step of each Scenario. You can also specify order if you are using multiple Before hooks.

@Before(order=1)
public void setUp1(){
// Some code here
}
@Before(order=2)
public void setUp2(){
// Some code here
}

Here, setUp1 will run first and then setUp2 will run.

After
The After hook runs after the last step of each Scenario. You may or may not pass the optional parameter “Scenario scenario”. This parameter can be used to get different information related to the scenario that has been executed. You can also specify order if you are using multiple After hooks.

@After(order=1)
public void tearDown1(Scenario scenario){
// Some code here
}
@After(order=2)
public void tearDown2(Scenario scenario){
// Some code here
}

Here, tearDown2 will run first and then tearDown1 will run.

BeforeStep and AfterStep
Code inside the BeforeStep and AfterStep hooks will run before and after each step inside the scenario respectively.

@BeforeStep
public void stepInitialization(){
// Some code here
}
@AfterStep
public void stepCleaning(){
// Some code here
}

TAGGED HOOKS

We can also select hooks according to specific Tags used in the scenario. To run a tagged hook only for certain scenarios, we can associate a Before or After hook with a Tag Expression. For example:

@After(“@FirstApplicationFeature”)
public void tearDown(Scenario scenario){
// Some code here
}

Tags are used to organize features and scenarios. A Feature, Scenario, Scenario Outline and Examples can contain multiple tags separated by whitespaces. Tags cannot be placed on top of Background and Steps.Tags mainly serve two purposes:

  • Running a subset of scenarios
  • Scoping hooks to a subset of scenarios

Let’s now see some examples:

@FirstApplicationFeature
Feature: Testing the FirstApplication

@Scenario1
Scenario: FirstApplication opens successfully in Desktop Chrome
  Given user opens the Chrome browser in desktop
  When user enters the application URL and searches
  Then the application gets opened successfully.

@Scenario2
Scenario: FirstApplication opens successfully in Mobile Web
  Given user opens the Chrome browser in mobile device
  When user enters the application URL and searches
  Then the application gets opened successfully.

Here, @FirstApplicationFeature is the tag at the Feature level and the tags @Scenario1 and @Scenario2 are at the Scenario level. The tags follow Inheritance principle which means that tags specified at Feature level is applicable for Scenario, Scenario Outline and Examples and tags specified at Scenario Outline level is applicable to Examples as well.

To perform Tag-based execution, we can use the Tag Expressions (along with parenthesis as optional). The Tag Expressions based on the above example can be:
@Scenario1 -> Only scenario tagged with @Scenario1 will run
@Scenario1 and @Scenario2 -> Scenarios tagged with both @Scenario1 and @Scenario2 will run
@Scenario1 or @Scenario2 -> Scenarios tagged with either of @Scenario1 or @Scenario2 will run
not @Scenario1 -> Scenario/ Features under all the tags except @Scenario1 will run

Credits:
https://cucumber.io/