Cucumber BDD (Part 2): Creating a Sample Java Project with Cucumber, TestNG, and Maven

Disclaimer: This is an extension to my previous tutorials: Behavior Driven Development (BDD) & Software Testing in Agile Environments and Cucumber BDD (Part 1): Starting with Feature Mapping. In this tutorial, we are gonna build a sample Java project with Cucumber, TestNG, and Maven for the better understanding of concepts studied in the first two tutorials.
Also I have implemented the steps discussed in this blog post series here: https://github.com/ashenwgt/awesome-bdd-with-cucumber. You can clone this project, modify my code, and start writing your test suite on top of it.

Introducing BDD Gear

Before getting started with BDD style, the following tools need to be setup in the development environment.

  • BDD Test Framework — to define application behavior in plain meaningful English text using a simple grammar defined by a domain specific language (DSL)– e.g. Cucumber (DSL: Gherkin), JBehave (DSL: Gherkin), Behat (DSL: Gherkin), Mocha (DSL: user-defined)
  • Test Runner — to automate and run the behavior tests– e.g. TestNG (Java), jUnit (Java), Mocha (JavaScript)
  • Optional IDE plugins — to enable BDD coding support in IDEs– e.g. Cucumber plugin for IntelliJ IDEA and Eclipse IDEs

Setting Up Development Environment

Step 1: Create Maven Project

Create a new Maven project from scratch and add the following dependencies and plugins to the pom.xml file. Choose ‘Enable Auto-Import’ Maven projects prompt at the beginning (to manually enable this, go to File > Settings > search and select ‘Maven’ > choose ‘Importing’ > enable ‘Import Maven projects automatically’).

Step 2: Add Cucumber for Java IntelliJ IDEA plugin

Go to File > Settings > Plugins > search ‘Cucumber for Java’ > Install

You may need to download dependencies of the plugin as well. If you are accessing the internet through a Proxy server, make sure you configure your IDE with proxy settings.

Step 3: Project Directory Structure

The directory structure of the sample project looks like the following. Note the structure of test features, step definitions, and the runner classes.

+---.idea
+---main
| +---java
| +---resources
\---test
| +---java
| | \---stepdefs
| | | |---ServiceHooks.java
| | | |---StepDefinitions.java
| | |---TestRunner.java
| \---resources
| \---features
| |---LoginProfile.feature
| |---UpdateProfile.feature
|---pom.xml
|---testng.xml

Step 4: Create testng.xml

Create testng.xml file in the project root and add below lines.

Step 5: Build Maven Project

Run the following command to build your project.

mvn clean install

Getting Started with Development

Step 1: Writing Features

Cucumber executes your .feature files in test/resources/featuresdirectory. These files contain executable specifications written in a domain specific language (DSL) called Gherkin which is a business-readable, plain-text, English-like language with a simple grammar. To specify business rules by real world examples, Gherkin use main keywords: Feature, Scenario, Given, When, Then, And, But, Background, Scenario Outline, Examplesand some extra syntax “”” (Doc strings), | (Data tables), @(Tags), # (Comments).

.feature file is supposed to describe a single feature of the system, or a particular aspect of a feature. It's just a way to provide a high-level description of a software feature, and to group related scenarios. A feature file gets the following format.

According to the above format, let’s write two feature files — LoginProfile.feature and UpdateProfile.feature to test its employee login and update profile functionalities in BDD style.
Login Profile UI
Update Profile UI

NOTE: The above scenarios are written to serve as a simple example and it is not the best approach to write scenarios. In production, always make sure that your scenarios are not tightly coupled with your tests. Your BDD scenarios should change only when the requirement changes, not when the the implementation changes (i.e. your BDD scenarios must drive the implementation, not the other way around).

As you can see, the above Gherkin language is a very easy-to-understand notation for a non-technical business person. But to write effective and optimum feature files, one needs a good practice and understanding on using Gherkin, BDD best practices, and Cucumber anti-patterns. Therefore, it is recommended to go through the following reference links to enhance your understanding on writing effective features.

  1. Cucumber official reference documentation on features — https://cucumber.io/docs/reference
  2. Cucumber anti-patterns (part one) — https://medium.com/@cucumberbdd/cucumber-anti-patterns-part-one-7ca25cc49426
  3. Cucumber anti-patterns (part two) — https://medium.com/@cucumberbdd/cucumber-anti-patterns-part-two-6f0b021c71fa

Step 2: Writing Test Runner

After writing the features, the test runner code is implemented. In the following code TestRunner.java class, note the @CucumberOptions. One can define the location of features, glue files (step definitions), and formatter plugins inside this Cucumber options. Refer Cucumber reference documentation to know more about available Formatter Plugins.

After writing features and test runner, you are ready to implement the step definitions. Thanks to Cucumber, the annotations and empty methods which map to the steps in feature files can be auto-generated. Run the following command in the terminal and find the step definition implementations in the terminal output. Copy those snippets and paste them inside your StepDefinitions.java class as shown in the next section.

mvn install

Step 3: Writing Step Definitions

Cucumber doesn’t know how to execute your scenarios out-of-the-box. It needs Step Definitions to translate plain text Gherkin steps into actionsthat will interact with the system. When Cucumber executes a Step in a Scenario, it will look for a matching Step Definition to execute.

When Cucumber matches a Step against a pattern in a Step Definition, it passes the value of all the capture groups to the Step Definition’s arguments.

Note that Cucumber does not differentiate between the five step keywords Given, When, Then, And and But.

Step 4: Writing Before/After Hooks

This is a cool option in Cucumber. But you need to a prior knowledge to understand Hooks. Cucumber hooks are more like utility functions which help to perform some activity before/after/during execution. With that, you must remember that not only Cucumber, but also the test runner also performs hook functions. According to the priority of each hook type, one can implement their activities in them appropriately.

Execution priority: TestNG’s @BeforeClass → Cucumber’s @Before → Cucumber Background → Cucumber Scenario → Cucumber’s @After → TestNG’s @AfterClass

Also note that each Cucumber Scenario executes independently, and Background steps mentioned in the feature file run before executing each Scenario.

In our example, one can implement initial configurations of the project in TestNG’s BeforeClass method. In cucumber’s Before hook, one can implement code to open web browser which is a prerequisite for all scenarios. In Background of each feature, one can implement steps to navigate to web site and/or login to account. In Cucumber’s After hook, one can take a snapshot of failure and close the browser.

Step 5: Grouping Features, Scenarios, and Step Definitions using Tags

Tags is a great way made for Cucumber power users to organize their features and scenarios. In above example, by changing tags = {“~@Ignore”} line totags = {“@UpdateProfile”}, one can choose run only the features and scenarios tagged with @UpdateProfile tag. A Scenario or feature can have as many tags as you like. Just separate them with spaces: @important @maintenance @db @auth

Any tag that exists on a Feature will be inherited by Scenario, Scenario Outline or Examples. Therefore, in above example, all the scenarios in Update Profile feature must run. By mentioning a tag before a Scenario (as same as above Feature case), one can choose to run a subset of scenarios with that tag. For more info, refer this official documentation: Cucumber Tags — https://github.com/cucumber/cucumber/wiki/Tags

Step 6: Running Tests

Run the following command to run the written tests on the sample project.

mvn verify

As the next steps, one can remove auto-generated comments and PendingExeception code inside Step Definition methods and start implementing the actual logic to test the product. For our sample project, we can write Selenium tests inside these Step definition methods.

Step 7: Generating Reports

This is one another cool option in Cucumber. If you carefully look at the pom.xml file, you can see Masterthought Cucumber reporting plugin. When you run above Maven verify goal, this plugin reads the Cucumber test report in JSON format (target/cucumber-reports/CucumberTestReport.json) and generate publishes a breath-taking Cucumber test report inside target/cucumber-reports/advanced-reports directory. Open all those HTML files in your browser and observe pretty html reports with charts showing the results of cucumber runs.

Okay, that’s it! And I know you have questions on this topic. Raise them in the comments section and I will definitely answer you ;-)

Additional Notes

Using Lambda Expressions for Step Definitions

Java Step Definitions are written in regular classes which don’t need to extend or implement anything. They can be written either using lambda expressions or method annotations. In the above, we used the method annotations. To use lambda expressions, use cucumber-java8 module instead of cucumber-java module in your pom.xml file.

<!-- 
Use only one of these two dependencies as per your preference
-->
<!-- 01. Dependency to use method annotations -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java</artifactId>
<version>1.2.5</version>
<scope>test</scope>
</dependency>
<!-- 02. Dependency to use lambda expressions -->
<dependency>
<groupId>info.cukes</groupId>
<artifactId>cucumber-java8</artifactId>
<version>1.2.5</version>
<scope>test</scope>
</dependency>

When you use the cucumber-java8 module, you can write the Step Definitions using lambdas:

package cucumber;

import cucumber.api.java8.En;

public class StepDefinitions implements En {
public StepDefinitions() {
Given("I have (\\d+) cukes in my belly", (Integer cukes) -> {
System.out.format("Cukes: %n\n", cukes);
});
}
}

Links for Official Docs

Cucumber creators are currently working on a new documentation for Cucumber. Their effort is to bring every detail about BDD and Cucumber to one documentation. Anyways, here are some links for Cucumber docs available for you at the moment.

  1. Cucumber Documentation: https://cucumber.io/docs
  2. Cucumber Reference: https://cucumber.io/docs/reference
  3. Cucumber JVM: https://cucumber.io/docs/reference/jvm#java
  4. Cucumber Book (New documentation) : https://docs.cucumber.io/
  5. Cucumber Wiki: https://github.com/cucumber/cucumber/wiki

Maven Build Failures

We noticed that sometimes you need additional Maven plugins (maven-compiler, maven-resources, …) to build the project successfully. Please note that this is not an issue in Cucumber. You may get these errors in other Maven projects as well. Try adding the missing plugins/dependencies and see whether it works. If you have issues, feel free to share them in the comments section, so that we can follow up with them.

Cucumber Support

If you need help using Cucumber you have a few different options:

Cucumber has a very active community and it is widely-spread among many platforms and forums. Feel free to raise your issues and get them solved with their support.


Be sure to follow me on Twitter @ashen_wgt and send me your feedback, any questions you have, and also let me know your experiences and I’ll check it out/retweet! You can also check my other articles on Agile Vision.