
Sharing State Between Cucumber Steps with Dependency Injection
When using Cucumber, if you want to share state between multiple step definition files, you’ll need to use dependency injection (DI). There are several options: PicoContainer, Spring, OpenEJB, etc. If you’re not already using DI, then I recommend PicoContainer. Otherwise, use the one that’s already in use, because you should only have one.
First thing you’ll need to do is add the dependency to your pom file:
1 2 3 4 5 |
<dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-picocontainer</artifactId> <version>{enter version number here}</version> </dependency> |
Next, create a new class that holds the common data. For example:
1 2 3 4 5 |
public class StepData { protected Response response; protected ValidatableResponse json; protected RequestSpecification request; } |
Then, in each of your step definition files that you want to use this common data, you can add a constructor that takes StepData as an argument. This is where the injection occurs. For example:
1 2 3 4 5 6 7 |
public class BookStepDefinitions { private StepData stepData; public BookStepDefinitions(StepData stepData) { this.stepData = stepData; } } |
Then you can use stepData to access all of the common fields needed across your step definition classes. For example, I can split this BookStepDefinitions class into two classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class BookStepDefinitions { private String ENDPOINT_GET_BOOK_BY_ISBN = "https://www.googleapis.com/books/v1/volumes"; private StepData stepData; public BookStepDefinitions(StepData stepData) { this.stepData = stepData; } @Given("a book exists with an isbn of (.*)") public void a_book_exists_with_isbn(String isbn){ stepData.request = given().param("q", "isbn:" + isbn); } @When("a user retrieves the book by isbn") public void a_user_retrieves_the_book_by_isbn(){ stepData.response = stepData.request.when().get(ENDPOINT_GET_BOOK_BY_ISBN); System.out.println("response: " + stepData.response.prettyPrint()); } @And("response includes the following in any order") public void response_contains_in_any_order(Map<String,String> responseFields){ for (Map.Entry<String, String> field : responseFields.entrySet()) { if(StringUtils.isNumeric(field.getValue())){ stepData.json.body(field.getKey(), containsInAnyOrder(Integer.parseInt(field.getValue()))); } else{ stepData.json.body(field.getKey(), containsInAnyOrder(field.getValue())); } } } } |
Then in another file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class SecondStepDefinitions { private StepData stepData; public SecondStepDefinitions(StepData stepData) { this.stepData = stepData; } @Then("the status code is (\d+)") public void verify_status_code(int statusCode){ stepData.json = stepData.response.then().statusCode(statusCode); } @And("response includes the following$") public void response_equals(Map<String,String> responseFields){ for (Map.Entry<String, String> field : responseFields.entrySet()) { if(StringUtils.isNumeric(field.getValue())){ stepData.json.body(field.getKey(), equalTo(Integer.parseInt(field.getValue()))); } else{ stepData.json.body(field.getKey(), equalTo(field.getValue())); } } } } |
This will allow you to share the state of the fields in StepData with all of your step definition files.
Oscar Barrios Torrero
Ibrahim
Can you also show how we can use it using Spring? Thanks.
Michael Smeeth
Hello Angie, i have a quick question. Well i have tried this example but i am getting java.lang.NullPointerException when assigning stepData.json = stepData.response.then().statusCode(statusCode);