Core Java

How to Share Data Between Steps in Cucumber

Cucumber is a popular BDD (Behavior-Driven Development) framework that helps bridge the communication gap between developers, testers, and business analysts. Often, while writing step definitions, we need to share data between steps. This is necessary when actions in one step affect subsequent steps in the scenario. Let us delve into understanding how Cucumber shares data between steps to maintain scenario continuity and improve test automation efficiency.

1. Setup

To use Cucumber with Spring, ensure you have the following dependencies in your pom.xml file:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>latest__jar__version</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>latest__jar__version</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Now, let’s configure Spring to work with Cucumber by creating a SpringConfiguration class.

1
2
3
4
5
6
7
8
9
package com.example.config;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
 
@Configuration
@ComponentScan(basePackages = "com.example")
public class SpringConfiguration {
}

2. Share Data Between Steps Using Spring

The recommended way to share data between steps in Cucumber with Spring is to use a ScenarioContext class, which acts as a shared storage.

2.1 Create ScenarioContext Class

The ScenarioContext class in Cucumber provides a way to share data between steps in a test scenario, ensuring continuity across different step definitions. It is implemented as a Spring-managed component using @Component, making it injectable into step definition classes. The class maintains a HashMap called dataMap to store key-value pairs dynamically. The set(String key, Object value) method allows storing any object, while the generic get(String key, Class<T> clazz) method retrieves and type-casts the stored data. This approach improves test modularity by preventing reliance on static variables, making test cases cleaner and more maintainable. With ScenarioContext, Cucumber steps can efficiently pass data, enabling seamless execution of complex test workflows.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package com.example.context;
 
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
 
@Component
public class ScenarioContext {
private Map<String, Object> dataMap = new HashMap<>();
 
public void set(String key, Object value) {
dataMap.put(key, value);
}
 
public <T> T get(String key, Class<T> clazz) {
return clazz.cast(dataMap.get(key));
}
}

2.1.1 Code Explanation

The given Java code defines a Spring component named ScenarioContext, which serves as a thread-safe container for storing and retrieving key-value pairs dynamically at runtime. The @Component annotation ensures that Spring manages this class as a bean, allowing it to be injected where needed. Internally, it utilizes a HashMap<String, Object> named dataMap to store data generically. The set method enables storing a value against a specified key, while the generic get method retrieves the value by casting it to the desired type, ensuring type safety. This utility is particularly useful in scenarios such as automated testing frameworks or request-scoped contexts where maintaining temporary test data or session-related information is required.

2.2 Inject ScenarioContext in Step Definitions

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.example.steps;
 
import com.example.context.ScenarioContext;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.When;
import io.cucumber.java.en.Then;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static org.junit.Assert.assertEquals;
 
@Component
public class MyStepDefinitions {
 
@Autowired
private ScenarioContext scenarioContext;
 
@Given("I set the user name to {string}")
public void iSetTheUserNameTo(String userName) {
scenarioContext.set("userName", userName);
}
 
@When("I retrieve the user name")
public void iRetrieveTheUserName() {
String storedName = scenarioContext.get("userName", String.class);
scenarioContext.set("retrievedUserName", storedName);
}
 
@Then("the retrieved user name should be {string}")
public void theRetrievedUserNameShouldBe(String expectedUserName) {
String actualUserName = scenarioContext.get("retrievedUserName", String.class);
assertEquals(expectedUserName, actualUserName);
}
}

2.2.1 Code Explanation

The given Java code defines a Spring component named MyStepDefinitions, which is a step definition class for Cucumber-based BDD (Behavior-Driven Development) testing. The class is managed by Spring as a bean due to the @Component annotation. It leverages dependency injection via the @Autowired annotation to access an instance of ScenarioContext, a shared storage utility for test data. The @Given step stores a user name in ScenarioContext, the @When step retrieves it and stores it again under a different key, and the @Then step verifies that the retrieved user name matches the expected value using assertEquals. This approach ensures data persistence across steps within a test scenario, making it useful for maintaining state between different phases of a BDD test execution.

2.3 Create a Feature File

Define the feature file in src/test/resources/features/share_data.feature:

1
2
3
4
5
Feature: Share data between steps
  Scenario: Storing and retrieving a user name
    Given I set the user name to "Yatin"
    When I retrieve the user name
    Then the retrieved user name should be "Yatin"

2.4 Create Cucumber Test Runner

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.example.runner;
 
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;
 
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/features",
glue = "com.example.steps",
plugin = {"pretty", "html:target/cucumber-report.html"}
)
public class CucumberTestRunner {
}

2.4.1 Code Explanation

The given Java code defines a test runner class named CucumberTestRunner, which is responsible for executing Cucumber tests in a JUnit environment. The @RunWith(Cucumber.class) annotation integrates Cucumber with JUnit, allowing test scenarios to be executed as JUnit tests. The @CucumberOptions annotation configures the test execution by specifying the feature files’ location using features = "src/test/resources/features", the step definitions package with glue = "com.example.steps", and the output format using the plugin option, which includes a readable console output (“pretty”) and an HTML report stored at target/cucumber-report.html. This setup ensures structured and automated execution of BDD test scenarios while generating reports for analysis.

2.4.2 Code Output

When the test suite is executed, the Cucumber scenario will run step by step, storing the username “Yatin” in ScenarioContext, retrieving it in the next step, and verifying it in the final assertion step. The console output would be:

1
2
3
4
5
Feature: Share data between steps
  Scenario: Storing and retrieving a user name
    Given I set the user name to "Yatin"  ✅ Passed
    When I retrieve the user name          ✅ Passed
    Then the retrieved user name should be "Yatin" ✅ Passed

The test successfully passes, demonstrating how data is shared across steps using a Spring-managed context. This approach ensures modular and reusable step definitions, making test automation more structured and maintainable.

3. Conclusion

Sharing data between steps in Cucumber can be efficiently handled using Spring’s dependency injection. The ScenarioContext class acts as a shared data store, ensuring smooth data transfer between step definitions. This approach makes step definitions modular, reusable, and easier to manage in large test suites.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button