In our previous chapter, we refactored the endpoints class to pass the Request headers as an instance of Endpoints class instead of passing them in each method. Subsequently, as a next step, we will learn to Share Test Context between our Step Definitions in our API Rest Assured Automation Framework.
Need for Sharing the Test Context between Cucumber Step Definitions
In real-life projects, the expectation is we build a scalable automation framework. Moreover, automation coverage increases as we add multiple scenarios. If a single step definition file contains all the step definitions, it clutters the step definition file. Additionally, it becomes non-maintainable. It is thus, imperative to refactor the step definitions separated under different classes.
In any scenario of a feature file in Cucumber, a series of steps get executed one after one. Every step of the scenario may possess a state which could prove useful to another step in the same scenario. Thus, the steps depend on the previously executed steps. Moreover, for this reason, sharing the state among the steps is needed.
In our framework, we have just one scenario for illustration purposes. But, it will grow over a while as we add numerous scenarios. Additionally, to keep the step definition file uncluttered and share the Test Context with all the Step Definition files, we will resort to the dependency injection Container PicoContainer. We have implemented this before in our former Cucumber tutorial with Selenium on sharing Test Context with Cucumber Step Definitions.
As a next step, we will learn the way to implement the Test Context sharing between our Step Definitions in our API Rest Assured Automation Framework.
How to Share the Test Context between Cucumber Steps using PicoContainer
We will follow the same steps as sharing Test Context with Cucumber Step Definitions to share the data state across steps:
- Firstly, add PicoContainer to the Project.
- Secondly, create a Test Context class to hold all the objects state.
- Thirdly, divide the Steps class into multiple steps classes.
- Fourth, write Constructor to share Test Context.
- Finally, run the Tests.
Add PicoContainer Library to the Maven Project
Add the maven dependency of dependency injection-PicoContainer into our project pom.xml. Additionally, you can find more details about the version etc. at [Maven Repository - Cucumber PicoContainer]
(https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer).
<!-- https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer -->
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
Create a Test Context class which will hold all the objects state
The Text Context class shall encompass all information your Steps files are using. In our framework, we are likewise using the information from the Endpoints class in the Steps file. So simply, we need to add an object of Endpoints class into this Test Context class.
To create a Test Context class,
-
Firstly, Right-click on the src/test/java and select New >> Package. Moreover, name it as a cucumber. All the Cucumber Helper classes will be in the same package hereon.
-
Secondly, Right-click on the above-created package and select New >> Class. Name it as TestContext.
TestContext.java
package cucumber;
import apiEngine.EndPoints;
public class TestContext {
private String BASE_URL = "https://bookstore.toolsqa.com";
private EndPoints endPoints;
public TestContext() {
endPoints = new EndPoints(BASE_URL);
}
public EndPoints getEndPoints() {
return endPoints;
}
}
Consequently, we kept the initialization of Endpoints in the constructor and created getEndPoints() for the object.
Divide the Steps class into multiple steps classes
We can logically divide our endpoints into Account Steps and Book Steps. Some of the necessary steps would include in the BaseStep.
Create three New Classes in the stepDefinitions package with the following names:
- AccountSteps
- BooksSteps
- BaseStep
Note: You may replace the Steps file with BaseSteps during clean up by moving the required step definitions to associated classes. Alternatively, you can create a BaseStep class, and once we completed this tutorial, then delete the Steps file as we no longer would be using it.
Write Constructor to share Test Context
We divided the Step Definitions to different Step classes. These classes commonly share the Endpoints class amongst themselves. Additionally, in real-life projects, we may have such multiple classes that have an extensive requirement for every Step class. Moreover, to create objects for all standard classes using a new operator, again and again, is not advisable.
If we add a constructor to our Steps class file and pass TestContext as a Parameter to the constructor, it would solve all the problems. In the TestContext object, we got everything required for the test.
Thus the AccountsSteps.java class will look as:
package stepDefinitions;
import apiEngine.model.requests.*;
import cucumber.TestContext;
import cucumber.api.java.en.Given;
public class AccountSteps extends BaseStep {
public AccountSteps(TestContext testContext){
super(testContext);
}
@Given("^I am an authorized user$")
public void iAmAnAuthorizedUser() {
AuthorizationRequest authRequest = new AuthorizationRequest("TOOLSQA-Test", "Test@@123");
getEndPoints().authenticateUser(authRequest);
}
}
Likewise, the changes for BooksSteps.java class is:
package stepDefinitions;
import apiEngine.IRestResponse;
import apiEngine.model.Book;
import apiEngine.model.requests.AddBooksRequest;
import apiEngine.model.requests.ISBN;
import apiEngine.model.requests.RemoveBookRequest;
import apiEngine.model.responses.Books;
import apiEngine.model.responses.UserAccount;
import cucumber.TestContext;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import io.restassured.response.Response;
import org.junit.Assert;
public class BooksSteps extends BaseStep {
public BooksSteps(TestContext testContext){
super(testContext);
}
private final String USER_ID = "9b5f49ab-eea9-45f4-9d66-bcf56a531b85";
private Response response;
private IRestResponse<UserAccount> userAccountResponse;
private Book book;
@Given("^A list of books are available$")
public void listOfBooksAreAvailable() {
IRestResponse<Books> booksResponse = getEndPoints().getBooks();
book = booksResponse.getBody().books.get(0);
}
@When("^I add a book to my reading list$")
public void addBookInList() {
ISBN isbn = new ISBN(book.isbn);
AddBooksRequest addBooksRequest = new AddBooksRequest(USER_ID, isbn);
userAccountResponse = getEndPoints().addBook(addBooksRequest);
}
@Then("^The book is added$")
public void bookIsAdded() {
Assert.assertTrue(userAccountResponse.isSuccessful());
Assert.assertEquals(201, userAccountResponse.getStatusCode());
Assert.assertEquals(USER_ID, userAccountResponse.getBody().userID);
Assert.assertEquals(book.isbn, userAccountResponse.getBody().books.get(0).isbn);
}
@When("^I remove a book from my reading list$")
public void removeBookFromList() {
RemoveBookRequest removeBookRequest = new RemoveBookRequest(USER_ID, book.isbn);
response = getEndPoints().removeBook(removeBookRequest);
}
@Then("^The book is removed$")
public void bookIsRemoved() {
Assert.assertEquals(204, response.getStatusCode());
userAccountResponse = getEndPoints().getUserAccount(USER_ID);
Assert.assertEquals(200, userAccountResponse.getStatusCode());
Assert.assertEquals(0, userAccountResponse.getBody().books.size());
}
}
Lastly, the BaseSteps.java class updates as:
package stepDefinitions;
import apiEngine.EndPoints;
import cucumber.TestContext;
public class BaseStep {
private static final String BASE_URL = "https://bookstore.toolsqa.com";
private EndPoints endPoints;
public BaseStep(TestContext testContext) {
endPoints = testContext.getEndPoints();
}
public EndPoints getEndPoints() {
return endPoints;
}
}
Run the Cucumber Test
Run the Tests as JUnit
We are all set now to run the updated Cucumber test. First, Right -Click on TestRunner class and Click Run As >> JUnit Test. Cucumber runs the script in the same way as Selenium WebDriver. Consequently, the result will display in the JUnit tab of the console.
Run the Tests from Cucumber Feature
To run the tests as a Cucumber Feature, right-click on the End2End_Test.feature file. After that, select the Run As>>Cucumber Feature.
The tests passed successfully with the changes we made for sharing the Test Context with Cucumber Step Definitions in our framework. Please try to implement the above changes in your framework, as explained above.
Our updated project folder structure of the framework will look likewise:
In the next chapter, we will introduce the concept of usage of Scenario Context in Framework to share the test data information specifically. Additionally, we will further segregate our steps into the Verification Steps from the Step classes.