Cucumber and Spring Boot Integration: Passing Arguments To Step Definitions Explained

Cucumber is a tool that supports Behavior-Driven Development (BDD). In this blog, you will learn how to pass arguments to step definitions when using Cucumber and Spring Boot. Enjoy!

Introduction

In a previous post, Cucumber was introduced as a tool that supports Behavior-Driven Development (BDD). Some of the features were explained, but not how to pass arguments to step definitions. In this blog, you will learn how you can do so. The application under test is a Spring Boot application. You will also learn how you can integrate the Cucumber tests with Spring.

The sources used in this blog are available on GitHub.

Do check out the following references for extra information:

Prerequisites

The prerequisites for this blog are:

Application Under Test

The application under test is a basic Spring Boot application. It consists of a Controller and a Service. The Controller serves a customer endpoint that implements an OpenAPI specification. The Service is a basic implementation, storing customers in a HashMap. A customer only has a first name and a last name, just to keep things simple. The API offers the following functionality:

Spring Integration

In order to enable the Spring integration, you add the following dependency to the pom:

XML
 
<dependency>
  <groupId>io.cucumber</groupId>
  <artifactId>cucumber-spring</artifactId>
  <version>7.14.0</version>
  <scope>test</scope>
</dependency>


The Spring Boot application must be in a running state; therefore, you need to run the Cucumber tests with the @SpringBootTest annotation. This will start the application and you will be able to run Cucumber tests for it. In order to do so, you create a class CucumberSpringConfiguration. Add the @CucumberContextConfiguration annotation so that the Spring integration is enabled. The Spring Boot application starts on a random port; therefore, you store the port in a system property so that you will be able to use it when you need to call the API.

Java
 
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CucumberSpringConfiguration {
    @LocalServerPort
    private int port;
 
    @PostConstruct
    public void setup() {
        System.setProperty("port", String.valueOf(port));
    }
}


The Cucumber step definitions will extend this class.

Tests can be run via Maven:

Shell
 
$ mvn clean verify


Test: Add Customer Using Arguments

The Add Customer test will add a customer to the customer list and will verify that the customer is added to the list. The feature file is the following. Do note that the first name (John) and last name (Doe) are within quotes. This way, Cucumber is able to recognize a string argument.

Plain Text
 
Scenario: Add customer
    Given an empty customer list
    When customer 'John' 'Doe' is added
    Then the customer 'John' 'Doe' is added to the customer list


The corresponding step definitions are the following.

Java
 
public class StepDefinitions extends CucumberSpringConfiguration {
 
    final int port = Integer.parseInt(System.getProperty("port"));
    final RestClient restClient = RestClient.create();
 
    @Given("an empty customer list")
    public void an_empty_customer_list() {
        ResponseEntity<Void> response = restClient.delete()
                .uri("http://localhost:"+ port + "/customer")
                .retrieve()
                .toBodilessEntity();
    }
 
    @When("customer {string} {string} is added")
    public void customer_firstname_lastname_is_added(String firstName, String lastName) {
        Customer customer = new Customer(firstName, lastName);
        ResponseEntity<Void> response = restClient.post()
                .uri("http://localhost:"+ port + "/customer")
                .contentType(APPLICATION_JSON)
                .body(customer)
                .retrieve()
                .toBodilessEntity();
        assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
    }
 
    @Then("the customer {string} {string} is added to the customer list")
    public void the_customer_first_name_last_name_is_added_to_the_customer_list(String firstName, String lastName) {
        List<Customer> customers = restClient.get()
                .uri("http://localhost:"+ port + "/customer")
                .retrieve()
                .body(new ParameterizedTypeReference<>() {});
        assertThat(customers).contains(new Customer(firstName, lastName));
    }
    ...
}


Note that the arguments are used to create a Customer object which is defined in the step definitions class. This class contains the fields, getters, setters, equals, and hashCode implementations.

Java
 
public static class Customer {
        private String firstName;
        private String lastName;
        ...
}


Test: Add Customers Using Arguments

When you want to add several customers, you can chain the same step definition by means of an And using different arguments. The feature file is the following, the step definitions remain the same.

Plain Text
 
Scenario: Add customers
    Given an empty customer list
    When customer 'John' 'Doe' is added
    And customer 'David' 'Beckham' is added
    Then the customer 'John' 'Doe' is added to the customer list
    And the customer 'David' 'Beckham' is added to the customer list


Test: Add Customer Using DataTable

The previous tests all started with an empty customer list. The next test will add some data to the customer list as a starting point. You can, of course, use the step definition customer firstName lastName is added and invoke it multiple times, but you can also use a DataTable. The DataTable must be the last argument in a step definition. The feature file is the following and the DataTable is used in the Given-clause.

Plain Text
 
Scenario: Add customer to existing customers
    Given the following customers:
      | John  | Doe     |
      | David | Beckham |
    When customer 'Bruce' 'Springsteen' is added
    Then the customer 'Bruce' 'Springsteen' is added to the customer list


In the implementation of the step definition, you now see that the arguments are passed as a DataTable. It is a table containing strings, so you need to parse the table yourself.

Java
 
@Given("the following customers:")
public void the_following_customers(io.cucumber.datatable.DataTable dataTable) {
 
    for (List<String> customer : dataTable.asLists()) {
        customer_firstname_lastname_is_added(customer.get(0), customer.get(1));
    }
}


Test: Add Customer Using Parameter Type

In the previous test, you needed to parse the DataTable yourself. Wouldn’t it be great if the DataTable could be mapped immediately to a Customer object? This is possible if you define a parameter type for it. You create a parameter type customerEntry and annotate it with @DataTableType. You use the string arguments of a DataTable to create a Customer object. You do so in a class ParameterTypes, which is considered as best practice.

Java
 
public class ParameterTypes {
    @DataTableType
    public StepDefinitions.Customer customerEntry(Map<String, String> entry) {
        return new StepDefinitions.Customer(
                entry.get("firstName"),
                entry.get("lastName"));
    }
}


The feature file is identical to the previous one, only the step definition has changed in order to have a unique step definition.

Plain Text
 
Scenario: Add customer to existing customers with parameter type
    Given the following customers with parameter type:
      | John  | Doe     |
      | David | Beckham |
    When customer 'Bruce' 'Springsteen' is added
    Then the customer 'Bruce' 'Springsteen' is added to the customer list


In the implementation of the step definition, you notice that the argument is not a DataTable anymore, but a list of Customer.

Java
 
@Given("the following customers with parameter type:")
public void the_following_customers_with_parameter_type(List<Customer> customers) {
 
    for (Customer customer : customers) {
        customer_firstname_lastname_is_added(customer.getFirstName(), customer.getLastName());
    }
}


Conclusion

In this blog, you learned how to integrate Cucumber with a Spring Boot application and several ways to pass arguments to your step definitions - a powerful feature of Cucumber!

 

 

 

 

Top