Data sharing is a very important concept in API testing. There are many ways to share the data among the tests. In this article, we will cover how to share data from one API to another API.
What Is API Chaining
API chaining is the technique where we need to feed the output of one API as input to another API. This process helps us to validate the full functionality of API, not just the individual endpoints. For example, creating a user ID needs to be used as input in updating users, deleting users, creating bookings for users etc.
Scope of Data Sharing
It is important to understand the scope of data sharing among API’s. We need to analyse and identify whether data is to be shared across only methods within the same class, if data sharing is required across different classes or do we need to feed the data across different Tests. Once the requirement is clear and the scope is defined then we can choose the appropriate method as per our project needs.
How to share data among different APIs
Several tools are available to facilitate the API chaining for example Postman, Katalon studio, Rest Assured Library etc. We will be using Rest Assured in this post. There are multiple approaches to sharing data from one API to another API. A few of them are as follows:
1) Data Sharing Using Class level Variable
Scenario: We will create a new user using Post call and then utilize the returned UserID to get the user details of the newly created user using Get Request.
1) Create a new user (Post Request)
2) Get User details (Get Request)
Steps to follow
1) Open IDE and create a new maven project.
2) Add Rest assured dependency in the pom.xml file.
3) Add Static imports for rest assured.
4) Create a class named UserProfileTest.
5) Create a global variable “UserId”.
6) Define the Base URL.
7) Create a method that contains the logic of creating a new user on hitting the post request.
8) Verify the status code.
9) Save the response into the response variable.
10) Fetch the “user-id” of the newly created user using the “jsonpath()” method and store it in the “UserId” variable.
public class UserProfileTest {
private static final String URL = "https://reqres.in";
private int UserId;
@Test(priority=1)
public void CreateNewUser () {
Response response = given ().contentType (ContentType.JSON)
.body (Body)
.when ()
.post (URL + "/api/users")
.then ()
.assertThat ()
.statusCode (201)
UserId =response.jsonPath().getInt("userid");
}
}
11) Create a new method in the same “UserProfileTest” class to fetch user details based on the user ID provided.
12) Fetch the user ID from the global variable “UserId” and pass it to the get request.
13) Maintain the execution order by using the testNg Priority to avoid the unnecessary issue.
@Test(priority=2)
public void getUserDetails () {
given ().when ()
.get (URL +"/"+UserId)
.then ()
.statusCode (200)
.and ()
.assertThat ()
.body ("data.id", equalTo (UserId));
}
After execution, at first post request will be hit and a new user with a unique user-id will be created. This user-id will be saved in the global variable. When the Get request executes it will fetch the user ID from the variable and will show all the details for that particular user.
The disadvantage of this method is that data can be shared only within this class. If we need to pass UserId to different test class methods, it won’t be possible to use private class variables.
2) By Creating a Common Class
Another data-sharing approach is to create a common class and declare all those global variables in that common class that are required for data sharing.
1) Create a new class and name it DataStore.
2) Add common variables to this class.
public class DataStore {
public static String User_Token;
public static String UserId;
public static int ActivityId;
}
3) Import the Datastore class in the UserProfileTest class.
4) Modify the UserProfileTest class to store and fetch data from the DataStore class.
public class UserProfileTest {
private static final String URL = "https://reqres.in";
private static final Logger LOG = LogManager.getLogger (UserProfileTest.class);
@Test(priority=1)
public void CreateNewUser () {
Response response = given ().contentType (ContentType.JSON)
.body (Body)
.when ()
.post (URL + "/api/users")
.then ()
.assertThat ()
.statusCode (201)
DataStore.UserId =response.jsonPath().getInt("userid");
Log.info("User Id of created user is:_"+DataStore.UserId);
}
@Test(priority=2)
public void getUserDetails () {
given ().when ()
.get (URL +"/"+DataStore.UserId)
.then ()
.statusCode (200)
.and ()
.assertThat ()
.body ("data.id", equalTo (UserId));
}
}
By using this approach we can share data among different classes. But this approach also has its limitations if we have to store and pass huge data then it’s not convenient to write 100 variables in the DataStore class and use it.
3) By Using TestNg ITestContext Interface
TestNg provides interfaces like ITestContext and ISuite, with the help of which data sharing is very easy. To use these interfaces TestNg dependency should be included in the project.
ITestContext:
This interface defines a test context that contains all the information for a given test run. To Use this approach, create a method and pass the ITestContext interface as a parameter.
Modify the UserProfileTest class as mentioned below code to use the ITestContext interface for data sharing.
@Test(priority=1)
public void CreateNewUser (ITestContext context) {
Response response = given ().contentType (ContentType.JSON)
.body (Body)
.when ()
.post (URL + "/api/users")
.then ()
.assertThat ()
.statusCode (201)
int UserId =response.jsonPath().getInt("userid");
context.setAttribute("UserId", UserId);
Log.info("User Id of created user is:_"+UserId);
}
@Test(priority=2)
public void getUserDetails (ITestContext context) {
given ().when ()
.get (URL +"/"+context.getAttribute("UserId"))
.then ()
.statusCode (200)
.and ()
.assertThat ()
.body ("data.id", equalTo (UserId));
}
This approach also has one limitation. Suppose the “CreateNewUser” test is on the “UserProfileTest1” class and “getUserDetails” is written in another class “UserProfileTest2” and these 2 classes are defined in 2 different test runs. For example :
<suite name="All Tests Suite">
<test thread-count="2" name="Test1">
<classes>
<class name="UserProfileTest1" />
</classes>
</test>
<test thread-count="2" name="Test2">
<classes>
<class name="UserProfileTest2" />
</classes>
</test>
</suite>
In the above case on execution, we will face a null pointer exception because as per the definition of ITestContext Interface data sharing is permissible in a single test run (<test> tag).
To mitigate this issue we can modify our script to pass the data at the suite level rather than the test level.
Steps:
1) Go to test class and modify “context.setAttribute(“UserId”, UserId)” to “context.getSuite().setAttribute(“UserId”, UserId)”.
2) Modify “context.getAttribute(“UserId”)” to “context.getSuite().getAttribute(“UserId”)”.
3) Modify the UserProfileTest1 class as the below-mentioned code.
public class UserProfileTest1 {
private static final String URL = "https://reqres.in";
private static final Logger LOG = LogManager.getLogger (UserProfileTest1.class);
@Test(priority=1)
public void CreateNewUser (ITestContext context) {
Response response = given ().contentType (ContentType.JSON)
.body (Body)
.when ()
.post (URL + "/api/users")
.then ()
.assertThat ()
.statusCode (201)
int UserId =response.jsonPath().getInt("userid");
context.getSuite().setAttribute("UserId", UserId)
Log.info("User Id of created user is:_"+UserId);
}
}
4) Update the UserProfileTest2 class as below-mentioned code.
public class UserProfileTest2 {
private static final String URL = "https://reqres.in";
private static final Logger LOG = LogManager.getLogger (UserProfileTest2.class);
@Test(priority=2)
public void getUserDetails (ITestContext context) {
given ().when ()
.get (URL +"/"+context.getSuite().getAttribute("UserId"))
.then ()
.statusCode (200)
.and ()
.assertThat ()
.body ("data.id", equalTo (UserId));
}
}
After successful updation of the script execute the tests and verify that APIs can share data at suite level without any exception.
Visit Here to learn how to build a Rest assured Framework step by step.
Conclusion
API Chaining is very essential for automating complex workflows. It also significantly improves efficiency by minimizing the number of interactions between the client and the server.