Cucumber + Spring boot 2 + Junit 5

 

When proper testing is not carried out, the cost of fixing an error in production is high. Therefore, implementing good testing techniques helps us sleep better at night. Including the development and business teams in automated testing would be a significant step forward in keeping code and requirements under control. This is where acceptance testing and BDD come into play; a solid implementation and requirement planning are essential in this stage of development.

As background, let’s understand that Cucumber is a TDD testing software that allows us to create automated tests based on easily understandable acceptance criteria by stakeholders involved in the process. We will implement it in this tutorial along with Spring Boot and JUnit.

To begin, we need a basic project with Spring Boot 2. In this tutorial, we will create it using Spring Initializer, an API that generates projects with their dependencies, simplifying the startup stage. Alternatively, you can use the Spring Boot plugin for IntelliJ or Visual Studio Code.

We will use the following configuration:

Initial configuration with Spring Boot Initializer

 

  1. Cucumber and Junit 5 configuration

 

In this step, we will add the Maven dependencies (libraries required for the proper functioning of Cucumber with Spring and JUnit).

 

1.1 Maven dependencies (pom.xml)

 

1.1.1 Cucumber JVM 

       <dependency>

           <groupId>io.cucumber</groupId>

           <artifactId>cucumber-java</artifactId>

           <version>7.8.1</version>

           <scope>test</scope>

       </dependency>

We can find the list of versions here.

 

1.1.2 Junit and Cucumber

      

       <dependency>

           <groupId>io.cucumber</groupId>

           <artifactId>cucumber-junit-platform-engine</artifactId>

           <version>7.8.1</version>

           <scope>test</scope>

       </dependency>

We can find the list of versions here.

 

1.1.3 Junit platform suite (@Cucumber version of Junit4 became obsolete)

      

       <dependency>

           <groupId>org.junit.platform</groupId>

           <artifactId>junit-platform-suite</artifactId>

           <version>1.9.0</version>

           <scope>test</scope>

       </dependency>

We can find the list of versions here.

 

1.1.4 Finally Spring and Cucumber

       <dependency>

           <groupId>io.cucumber</groupId>

           <artifactId>cucumber-spring</artifactId>

           <version>7.8.1</version>

           <scope>test</scope>

       </dependency>

      

We can find the list of versions here.

 

  1. Definition and implementation of “Features”

For this definition, it’s common to use Gherkin; being high-level, it’s not so technical, thus it’s possible to involve business personnel.

We create demo.feature file src/test/java/<paquete>/cucumber/features with the following content:

   Feature: the hello endpoint can be retrieved

     Scenario: client makes call to GET greeting endpoint

       Given client wants to write username

       When client calls greeting endpoint

       Then client receives hello text and username

 

 

The content should not contain many technical definitions; let’s remember that the objective is to involve the entire team.

 

Definition of steps:

 

Given: Establishing a situation in which we configure the test.

When: Action taken or an event occurs.

Then: Expecting something to happen or being able to verify it.

 

  1. Setting up entry points

The following class serves as the entry point for Cucumber to execute the tests. It can be named anything, but always considering conventions.

We create CucumberTest.java class in src/test/java/<paquete>/cucumber with the following content:

   @Suite

   @IncludeEngines(“cucumber”)

   @SelectClasspathResource(“com/sevolutivo/demo/cucumber/features”)

   @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = “com.sevolutivo.demo.cucumber.stepDefs”)

   public class CucumberTest {

   }

 

@Suite y @IncludeEngines(“cucumber”) they are the equivalent of the old @Cucumber annotation, but it became obsolete with version 7 of Cucumber. 

 

In JUnit 5, we can define the cucumber options in the junit-platform.properties file, located in the tests resources folder.

 

@SelectClasspathResource(“<paquete>/cucumber/features”) tell us within the resources folder where we have .feature files.

 

@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = “<paquete>.cucumber.stepDefs”) we are telling it where the glue classes are located, that is, the steps.

 

  1. Additional optional configuration (junit-platform.properties)

 

We create junit-platform.properties file in src/test/resources with the following content:

 

 cucumber.publish.quiet=true

 cucumber.publish.enabled=false

 cucumber.plugin=pretty, html:target/cucumber-reports/Cucumber.html,   json:target/cucumber-reports/Cucumber.json,junit:target/cucumber-reports/Cucumber.xml

 

In the above example, we are removing the banner that appears when running cucumber and instructing it not to publish the results to its cloud service. Additionally, we are generating reports locally in HTML, JSON, and XML formats in the target/cucumber-reports directory.

 

  1. Spring configuration

 

Let’s configure Cucumber to make use of the Spring context with the @SpringBootTest annotation. We’ll also inject TestRestTemplate for making Rest calls.

 

We create CucumberSpringConfiguration.java file in src/test/java/<paquete>/cucumber:

 

@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)

   public class CucumberSpringConfiguration {

       @Autowired

       protected TestRestTemplate testRestTemplate;

   }

 

To verify proper functionality, run ./mvnw clean verify in the console, and the result should be similar to the following content:

 

 

We can see that it doesn’t find the step definitions and suggests implementing them according to its recommendations.

 

  1. Implementation of steps (steps or glue code)

 

The steps are the mapping of each step of the scenario defined in the .feature file. This class has the @CucumberContextConfiguration notation, which makes Cucumber use it as the Spring context configuration.

 

We create DemoSteps.java file in src/test/<paquete>/cucumber/stepDefs:

 

   @CucumberContextConfiguration

   public class DemoSteps extends CucumberSpringConfiguration {

       private String username;

       private ResponseEntity response;

       @Given(“client wants to write username”)

       public void clientWantsToWriteAGreeting(){

           username = “user001”;

       }

       @When(“client calls greeting endpoint”)

       public void clientCallsToGreetingEndpoint() {

           response = testRestTemplate.getForEntity(“/greeting/” + username, String.class);

       }

       @Then(“client receives hello text and username”)

       public void clientReceivesHelloTextAndGreeting() {

           Assertions.assertEquals(“hello “ + username, response.getBody());

       }

   }

 

  1. Rest API controller implementation

 

We create controller class DemoController.java with one GET method /greeting in  src/main/java/<paquete>/web that return greeting string:

 

   @RestController

   public class DemoController {

       @GetMapping(“/greeting/{username}”)

       public String greeting(@PathVariable String username){

           return “hello “ + username;

       }

   }

 

  1. Execution

Once the entire implementation is ready, execute ./mvnw clean verify and we will obtain the following content:

 

 

If IntelliJ IDEA is used, running the implemented Cucumber tests will result in something similar to this:

 

 

  1. Conclusions

 

Acceptance tests are an important tool if the goal is to integrate the business team and the development team, thus improving the formulation of scenarios, requirements, and analysis cases.

 

In this tutorial, we have outlined a basic case of implementation and the configuration of Cucumber, Spring Boot, and JUnit 5 to obtain a basic or reference architecture for future implementations.

 

The source code can be found on github.com.

leave a comment