Setup and Run Java Spring Boot Project with Cucumber

In this chapter, let us setup a Java, Spring Boot project with Cucumber, create a simple feature file, implement the step definitions for the Given, When, Then steps in that feature file and run it. Just follow along the chapter and try doing all the steps. This will give some familiarity to Cucumber and Behavior Driven Development (BDD). We will learn the concepts as we go.


1. Prerequisites

For this tutorial, let us use the following

1. Java 11 LTS
2. Cucumber 6.8.1
3. Spring Boot 2.4.4
4. Intellij IDE (You may use your favorite IDE)


2. Create a Spring Boot Project using Spring Initializer

There are 2 ways you can create a Spring Boot project.

  1. Using Spring Initializer website Spring Initializer
  2. Use Spring Initializer in your IDE

In this chapter, let us use the option 1 (Using Spring Initializer website). Click the Spring initializer link to open Spring Initializer website and choose Maven Project, Java Language, Spring Boot version, Java version and all dependencies stated in the below image.

Spring Initializer

After choosing Java version, Spring Boot version and above said dependencies, click on Generate button.

Spring Initializer will trigger a zip file download, you may unzip it and import/open the project into you favorite IDE.


3. Add Maven Dependencies

Add the following Cucumber dependencies to pom.xml file.

<!--
Java implementation of Cucumber BDD framework
https://github.com/cucumber/cucumber-jvm
-->
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-java</artifactId>
  <version>6.8.1</version>
  <scope>test</scope>
</dependency>

<!--
Library to use JUnit Platform to execute Cucumber Scenarios
This also helps IDE to discover and run Cucumber tests
https://github.com/cucumber/cucumber-jvm/tree/main/junit-platform-engine
-->
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-junit-platform-engine</artifactId>
  <version>6.8.1</version>
</dependency>

<!--
Library to access Spring Application Context and share
state between cucumber step execution
-->
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-spring</artifactId>
  <version>6.8.1</version>
  <scope>test</scope>
</dependency>

<!--
  Spring doesn't include the right version for surefire to pickup
  TODO: Remove once the surefire or spring is upgraded
  [Recommended by Cucumber JVM committers]
-->
<dependency>
  <groupId>org.junit.platform</groupId>
  <artifactId>junit-platform-commons</artifactId>
  <version>1.7.1</version>
  <scope>test</scope>
</dependency>

4. Setup Cucumber Spring Context Configuration

Navigate to following location and create a class to declare the Cucumber Spring Context Configuration annotation.

cd src/test/java/com/madrascoder/cucumberbooksample/
touch CucumberSpringContextConfiguration.java

Add following code,

import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@CucumberContextConfiguration
public class CucumberSpringContextConfiguration {}

This configures the application context that Cucumber should use. In this case, it is implicit, test will use Spring Boot application main class automatically.


5. Create a Class to Trigger Cucumber Test

Navigate to following location and create a new class,

cd src/test/java/com/madrascoder/cucumberbooksample
touch RunCucumberTest.java

Add following code,

package com.madrascoder.cucumberbooksample;

import io.cucumber.junit.platform.engine.Cucumber;

@Cucumber
public class RunCucumberTest {}

RunCucumberTest class helps IDE to discover and run Cucumber feature files and step definitions in the respective package. You may right click to run all Cucumber tests using this class.

This is automatically run by maven as well.


6. Configure Cucumber Plugin Using JUnit Properties

Navigate to following location and create a new properties file,

cd src/test/resources
touch junit-platform.properties

Add the following properties,

# Options if you don't want to publish test umber reports to 
# https://reports.cucumber.io cloud
cucumber.publish.quiet=true
cucumber.publish.enabled=false

# Cucumber Test Report Generation Plugins. Html report is generated 
# and packaged along with the application.
cucumber.plugin=pretty,html:target/classes/static/features/index.html

For other available Cucumber properties, you may refer to Cucumber Properties


7. Let’s Create our First Feature File

In this chapter and the next one, we will use a feature file that is not something we do in a day to day work. This is just to make you understand the concepts. In later chapters we will create feature files for ‘HR Software’. These will be more realistic and similar to what we do in our daily work. So please hold on.

Navigate to following location and Create a file named 1000-sum-of-numbers.feature,

cd src/test/resources/com/madrascoder/cucumberbooksample
touch 1000-sum-of-numbers.feature

Important Note: Remember, you have to create multiple directories and not one directory as com.madrascoder.cucumberbooksample like what you do for package. This is src/test/resources directory and not src/test/java so don’t create directories like how you create package in Java.

Add the following Gherkin or feature to it,

Feature: Addition
  Scenario: Sum of two numbers
    Given first number is 10
    And second number is 20
    When user executes sum function
    Then the sum is 30

Note: Package structure of step definition Java classes under src/test/java should match with the directory structure of feature files in src/test/resources as Cucumber looks for feature files there.

Feature file is created using something called Gherkin. This is one of the ways to represent the requirement or executable specifications of the software. A file that contains a feature of the software product is called a feature file. Important keywords here are Feature, Scenario, Given, And, When, Then, Scenario Outline, Examples.

Feature: Every feature file contains a feature of the software product. A feature may contain one or more Scenarios.

Scenario: It is nothing but a representation of test case or behavior of the software using Given, When, Then Gherkin words. You may also use And and But as Gherkin words. Imagine these as reserved words of the feature file.

Given, When, And, Then: These are the steps in the test case or scenario. Each Given/When/And/But/Then step in feature file will have a corresponding Java method in a class called *StepDefinitions.java. Name of the class can be anything but the methods should be annotated with @Given or @And or @When or @Then. StepDefinition methods are annotated with @Given/@When/@Then annotations. When a feature file or scenario is run, Cucumber finds the matching step definition method for each step and executes the same.

In a nutshell, if the pattern of the step matches with the value of the @Given/@When/@Then annotation, corresponding method is executed. Hence, there can be only one matching method per step in feature file.

By now, your mind may be thinking that creating one method per step may need a lot of code and it sounds crazy. But, don’t worry there are smart ways to create one step definition method and reuse it for multiple steps using Cucumber Expressions. We will learn how to use Cucumber Expressions in the coming chapters.


8. Create Step Definitions

Let’s create the step definitions file for 1000-sum-of-numbers.feature

Navigate to following location and create class for adding step definition methods,

cd src/test/java/com/madrascoder/cucumberbooksample/stepdefinitions
touch AdditionStepDefinitions.java

Add the following code,

import static org.assertj.core.api.Assertions.assertThat;

import io.cucumber.java.en.And;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;

public class AdditionStepDefinitions {

  private int firstNumber;
  private int secondNumber;
  private int calculatedSum;

  @Given("first number is {int}")
  public void firstNumberIs(int firstNumber) {
    this.firstNumber = firstNumber;
  }

  @And("second number is {int}")
  public void secondNumberIs(int secondNumber) {
    this.secondNumber = secondNumber;
  }

  @When("user executes sum function")
  public void userExecutesSumFunction() {
    calculatedSum = firstNumber + secondNumber;
  }

  @Then("the sum is {int}")
  public void theSumIs(int expectedSum) {
    assertThat(calculatedSum).isEqualTo(expectedSum);
  }
}

Annotations @Given, @And, @When, @Then are synonymous, they can be used interchangeably. Important part is the value of these annotation. These values corresponds to the steps in feature file. You may have noticed {int} in annotation values. We will learn more about expressions {int}, {string} etc., in future chapters. For now, imagine the text at location of {int} in feature file will be automatically converted to int and supplied as argument to the corresponding method.

Compare the steps that starts with Given, When, Then in feature file with the methods in the step definitions class file. This is how the feature file and Java code is connected.


9. Project Structure

If you had done all the steps from the beginning, you may have the following project structure. Take few minutes to compare your project with what is stated below, this way when you run the Cucumber test in next step, it will work as expected.

➜  cucumber-book-sample tree
.
├── HELP.md
├── cucumber-book-sample.iml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── madrascoder
    │   │           └── cucumberbooksample
    │   │               └── CucumberBookSampleApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        ├── java
        │   └── com
        │       └── madrascoder
        │           └── cucumberbooksample
        │               ├── CucumberSpringContextConfiguration.java # Added
        │               ├── RunCucumberTest.java                    # Added
        │               └── stepdefinitions                         # Added
        │                   └── AdditionStepDefinitions.java        # Added
        └── resources
            ├── com                                                 # Added
            │   └── madrascoder                                     # Added
            │       └── cucumberbooksample                          # Added
            │           └── 1000-sum-of-numbers.feature             # Added
            └── junit-platform.properties                           # Added

10. Run Cucumber Test

There are multiple ways to trigger/run Cucumber tests, here is the list

You may do mvn verify from your terminal or command prompt as stated below.

cd cucumber-book-sample 
mvn clean verify
...

2021-04-12 20:46:51.552  INFO 55910 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 59841 (http) with context path ''
2021-04-12 20:46:51.560  INFO 55910 --- [           main] c.m.c.CucumberSpringContextConfiguration : Started CucumberSpringContextConfiguration in 3.771 seconds (JVM running for 5.486)

Scenario: Sum of two numbers      # com/madrascoder/cucumberbooksample/1000-hello-world.feature:4
  Given first number is 10        # com.madrascoder.cucumberbooksample.stepdefinitions.AdditionStepDefinitions.firstNumberIs(int)
  And second number is 20         # com.madrascoder.cucumberbooksample.stepdefinitions.AdditionStepDefinitions.secondNumberIs(int)
  When user executes sum function # com.madrascoder.cucumberbooksample.stepdefinitions.AdditionStepDefinitions.userExecutesSumFunction()
  Then the sum is 30              # com.madrascoder.cucumberbooksample.stepdefinitions.AdditionStepDefinitions.theSumIs(int)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.666 s - in Addition
...

[OR]

You may right click on RunCucumberTest.java and choose Run/Debug in IntelliJ

RunCucumberTest.java

[OR]

You may right click on 1000-sum-of-numbers.feature under resources and choose Run in IntelliJ

RunCucumberTest.java


Conclusion

In this chapter, we created a Java, Spring Boot project, added Cucumber dependencies, created necessary configuration to bootstrap Spring Boot Test context configuration, created a class to run Cucumber tests, created feature file, create step definitions and finally run the test using various ways. In the next chapter, we will learn different ways of creating feature files and corresponding step definitions.


References

Gherkin

IntelliJ Cucumber Plugin


Credits

Photo by Braden Collum on Unsplash


Previous Chapter | Scroll Up to Top | Table of Contents | Next Chapter