DEV Community

Bushra Alam
Bushra Alam

Posted on

BDD and Cucumber

Cucumber is a tool that facilitates BDD. And so let's first see what BDD is.

BDD

BDD is Behavior Driven Development.

BDD is a collaborative way of working for teams so that the gap between business people and technical people is reduced to achieve shared understanding of the system to be built.

BDD is a collection of practices that is build upon agile ways of working. BDD comprises of three steps (done repeatedly):
Discover - Discover the behavior of the system
Formulate - Formulate the specification in business-readable language
Automate - Automate the formulated specification to verify that the system actually behaves as expected.


CUCUMBER

Cucumber is a test automation tool that supports Behavior Driven Development (BDD).
Let's understand each of the BDD steps and see where Cucumber fits in.

Discover

For Discovery we arrange what is called 'Three Amigos Meeting'. The three amigos are: Product owner, developer and tester.
They sit together, discuss a business requirement (user story), illustrate with examples, ask and answer as many questions as they can. At the end of the meeting, all the three amigos should be on the same page i.e have the shared understanding of the user story.

Formulate

The next step is Formulation, which involves creating a document containing multiple scenarios for a given business requirement or feature that has crossed the discovery phase. The document needs to be such that it could easily be understood by the three amigos as well as by Cucumber. Now because the document has to be in the business-readable language (for amigos) that could also be executed (by cucumber), a special language called Gherkin is used.
The document is called Feature file. It typically contains multiple scenarios (test cases) for one feature and looks something like this:

Feature: Process refunds
  Scenario: Customer has their receipt
    Given the customer has purchased a kettle for €10
      And they have their receipt
     When the sales assistant processes the refund
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

Automate

Once a feature file has been created for a business requirement or a feature, the final step is Automation. To automate the scenarios Step Definition files are created. It has a function corresponding to each 'Given', 'When', 'Then' of each scenario specified in the Feature file.

So, Cucumber is a tool that understands documents written in Gherkin syntax and helps to automate them through Step Definition files.


Setup

Let's setup Cucumber and get our hands dirty.

Pre-Setup

  1. Download and Install a Programming Language - Cucumber supports many languages like Java, JS, Ruby and more. We have picked Java for this tutorial.
  2. Download and setup a build tool - We will be using Maven
  3. Download and Install an IDE
  4. Create a Maven quickstart project

Setup Cucumber

  1. Add the following dependencies in pom.xml
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>1.2.6</version>
            <scope>test</scope>
        </dependency>

Feature File

To start with a Cucumber project, the first step is to create a Feature file. Feature files have .feature extension and are written in Gherkin language.

Gherkin

Lets quickly see what Gherkins is.

  • Scenarios written in Gherkin could be read and understood easily by the business owner, developer and tester as they are basically plain english sentences.
  • Infact Gherkin is localized for over 70 spoken languages.
  • Gherkin is understood and could be executed by Cucumber
  • Each line must start with a Gherkin keyword.

    • The primary keywords are:
      • Feature
      • Rule (as of Gherkin 6)
      • Scenario or Example
      • Given, When, Then, And, But (steps)
      • Background
      • Scenario Outline or Scenario Template
      • Examples
    • There are a few secondary keywords as well:
      • """ (Doc Strings)
      • | (Data Tables)
      • @ (Tags)
      • # (Comments)

Let's see what each of these keywords are used for:

Feature: provide a high-level description of a software feature, and to group related scenarios.

Rule: represent one business rule that should be implemented. It provides additional information for a feature.

Scenario or Example: This is a concrete example that illustrates a business rule. It consists of a list of steps.

Each step starts with Given, When, Then, And, or But.

Given: the initial context of the system - the scene of the scenario.

When: an event, or an action. This can be a person interacting with the system, or it can be an event triggered by another system.

Then: an expected outcome, or result.

And, But: If you have successive Given’s, When’s, or Then’s, you could use And or But based on the context.

Background: add some context to the scenarios in the feature.

Scenario Outline: to run the same Scenario multiple times, with different combinations of values.

Examples: A Scenario Outline must contain Examples section containing all the different values to run the Scenario Outline with.

We will learn about secondary keywords as we work with them in a while

Let's see a simple scenario:

Feature: Process refunds
  Scenario: Customer has their receipt
    Given the customer has purchased a kettle for €10
      And they have their receipt
     When the sales assistant processes the refund
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

This is very straight forward - technical as well as nontechnical person can understand this. This is the beauty of Gherkin.


Step Definition

To run the scenarios in the feature file, a step definition file with a function for each Given, When, Then is required.

@RunWith(Cucumber.class)
public class MyStepDefinitions {

    @Given("^the customer has purchased a kettle for €10$")
    public void the_customer_has_purchased_a_kettle_for_10() {
        //code
    }

    @When("^the sales assistant processes the refund$")
    public void the_sales_assistant_processes_the_refund() {
        //code
    }

    @Then("^the stock inventory for kettles is incremented by 1$")
    public void the_stock_inventory_for_kettles_is_incremented_by_1() {
        //code
    }

    @And("^they have their receipt$")
    public void they_have_their_receipt() {
        //code
    }

    @And("^the customer's card is credited with €10$")
    public void the_customers_card_is_credited_with_10() {
        //code
    }

}

Passing Data

Let's now see how to pass data to these scenarios.

1. Regular Expression

Regular Expressions could be used when multiple scenarios are similar but they work with different data.
Say, for example, for Feature 'Process refund', you have a scenario where customer has their bank statement claiming they purchases a kettle for €10 from the store. In this case you would like to reuse the following function and not create a fresh one for 'bank statement'

    @And("^they have their receipt$")
    public void they_have_their_receipt() {
        //code
    }

The Feature file would look something like this:

Feature: Process refunds
  Scenario: Customer has their receipt
    Given the customer has purchased a kettle for €10
      And they have their "receipt"
     When the sales assistant processes the refund
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

  Scenario: Customer has their bank statement
    Given the customer has purchased a kettle for €10
      And they have their "bank statement"
     When the sales assistant processes the refund
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

The function 'they_have_their_receipt()' in Step Definition would change to Regular Expression:

    @And("^they have their \"([^\"]*)\"$")
    public void they_have_their_something(String strArg1) {
        //code
    }

2. Data Table

When you have multiple parameters to pass to the function

Feature File:

Feature: Process refunds
  Scenario: Customer has their receipt
    Given the customer has purchased a kettle for €10
      And they have their receipt
     When the sales assistant processes the refund
      And user fills the process refund form
      | John | Smith | US | 987654321 | 
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

The function in Step Definition would look like this:

    @And("^user fills the process refund form$")
    public void user_fills_the_process_refund_form(DataTable data) { 
        //To access the values
        List<List<String>> obj = data.raw();
        System.out.println(obj.get(0).get(0)); // 1st row, 1st column
        System.out.println(obj.get(0).get(1)); // 1st row, 2nd column
        System.out.println(obj.get(0).get(2)); // 1st row, 3rd column
        System.out.println(obj.get(0).get(3)); // 1st row, 4th column       
    }

3. Scenario Outline & Example keyword

Use when you want to run the same scenario with multiple data sets

Feature File:

Feature: Process refunds
  Scenario Outline: Customer has their receipt
    Given the customer has purchased something
      And they have their receipt
     When the sales assistant processes the refund
     Then the stock inventory for <product> is incremented by <quantity>
      And the customer's card is credited with €<price>

    Examples: 
      | product | quantity | price | 
      | kettle  | 1        | 10    | 
      | cup     | 6        | 50    | 
      | mug     | 2        | 30    | 
      | glass   | 12       | 40    |

Following two functions in Step Definition would be updated:

    @Then("^the stock inventory for (.+) is incremented by (.+)$")
    public void the_stock_inventory_for_is_incremented_by(String product, String quantity) {
        System.out.println(product);
        System.out.println(quantity);
    }

    @And("^the customer's card is credited with €(.+)$")
    public void the_customers_card_is_credited_with_(String price) {
        System.out.println(price);
    }

Tagging

  • You can tag your scenarios. Tagging a scenario helps you to run only the scenarios that belong to a certain tag.
  • You can have multiple tags for a single scenario
  • You can run multiple tags together
Feature: Process refunds
  @RegressionTest
  Scenario: Customer has their receipt
    Given the customer has purchased a kettle for €10
      And they have their receipt
     When the sales assistant processes the refund
     Then the stock inventory for kettles is incremented by 1
      And the customer's card is credited with €10

Executing Test

To run the feature files, an empty class is created with @RunWith(Cucumber.class) annotation. All the configurations are put in the @CucumberOptions.

   @RunWith(Cucumber.class)
   @CucumberOptions(
        features = "src/test/java/features",    // feature files to run
        glue = "stepDefinitions",       // step definitions
   )
   public class RunTest 
   {
        // This class will be empty 
   }

@CucumberOptions can have many more configurations, for example:
tags — to run only the scenarios with specified tags
plugin — to specify different formatting options for the output reports
dryRun = true — to check if you have stepdefinition for each line in your feature file
monochrome = true — to receive clean and readable output in your console
strict = true — to receive errors for things like missing step definition instead of just passing the scenarios and marking those steps as skipped

   @RunWith(Cucumber.class)
   @CucumberOptions(
        features = "src/test/java/features",    // feature files to run
        glue = "stepDefinitions",       // step definitions
        tags = "@RegressionTest, @SanityTest",
        plugin = {"pretty", "html:target/htmlreport", "json:report.json", "junit:report.xml"},
        dryRun = true,
        monochrome = true,
        strict = true
   )
   public class RunTest 
   {
        // This class will be empty 
   }

Learn how to integrate Cucumber with Cypress

Top comments (2)

Collapse
 
fobo66 profile image
Andrey Mukamolov

Thanks for the article! But you're using slightly outdated version of Cucumber. Current version is 5.5.0, and package name was changed to io.cucumber, causing confusion. Please check the repo for details. Also here is Maven link

The way of writing step definitions is the same, but tests run a bit faster and reports are more informative in the new version

Collapse
 
bushraalam profile image
Bushra Alam

Thanks Andrey... appreciate your comment. I will update the post soon.