API testing is an important part of the software development and testing process. Several tools and frameworks are available to test and automate API testing to ensure the robustness and performance of the API. In this article, I will guide you on creating a REST Assured Framework from scratch to streamline your API testing process.
What is Rest Assured?
Rest Assured is an open-source and easy-to-use Java DSL used by developers and automation engineers to test rest services. Rest assured framework also provides the flexibility to write BDD-style scripts to enhance readability. Integrating rest assured into a ci/cd pipeline is easy.
Rest Assured Prerequisites:
Before delving into the implementation, ensure that the following tools are installed:
- Download and install Eclipse.
- Download and Install JDK and Maven.
- Create a new maven project in the IDE.
1) Setup Rest Assured
Open your IDE and create a new Maven project.
1. a)Configure Dependencies:
Open the newly created project and add the following dependencies in the pom.xml file.
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
2)Choose APIs to work with
I used Spotify API for the demo in one of the previous articles. I will use the same API here.
3)Automate Simple Queries Using Rest Assured Java
We will automate CRUD (CREATE, READ, UPDATE and Delete operations) using GET, POST, PUT and Delete HTTP verbs.
a) Implementing Get Query(Fetch User details)
API reference: Get the current user’s profile
Endpoint: https://api.spotify.com/v1/me
Output: Id
The GET method is used to request a resource from the server. Create a class UserProfileTest and write a simple test using REST Assured to make a GET request.
@Test
public void GetUserId() {
RestAssured.given()
.baseUri("https://api.spotify.com/").basePath("v1/me")
.header("Authorization", "Bearer " +Token )
.when().get("/me")
.then()
.statusCode(200)
.statusLine("HTTP/1.1 200 OK")
// To verify country in Response Payload
.body("country", equalTo("IN"));
}
This is the simple code to execute GET requests and validate the status code and the country field in the response.
b) Implementing Post Method (Create a playlist) for the User
Endpoint: https://api.spotify.com/v1/users/{userid}/playlists
Output: PlaylistId
Request Payload:
{
"name": "Test Playlist 0078",
"description": "New playlist description",
"public": true,
}
Create a new test in the class for post requests.
@Test
public void createPlaylist() {
String userId = "your-user-id";
String requestBody = "{}";
// Send the POST request
RestAssured
.given()
.baseUri("https://api.spotify.com/v1/users/" + userId+"playlists")
.contentType(ContentType.JSON)
.header("Authorization", "Bearer your-access-token") // Include your Spotify access token
.body(playlistRequest)
.when()
.post("/playlists")
.then()
.statusCode(201); // Assuming a successful creation returns HTTP status code 201
}
Additionally, we can retrieve and parse the JSON response and use it for API chaining. Import the response package from the Rest Assured library and create a reference to store the response of the Post request.
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import org.testng.annotations.Test;
@Test
public void createPlaylist() {
String userId = "your-user-id";
Response response = RestAssured
.given()
.baseUri("https://api.spotify.com/v1/users/" + userId)
.contentType(ContentType.JSON)
.header("Authorization", "Bearer your-access-token") // Include your Spotify access token
.body(playlistRequest)
.when()
.post("/playlists")
.then()
.statusCode(201)
.extract()
.response();
String playlistId = response.jsonPath().get("id");
System.out.println("Created Playlist ID: " + playlistId);
}
c) Update (Update playlist)
Endpoint: https://api.spotify.com/v1/playlists/{playlistid}
Request Payload
Refer to the updated request body below.
{
"name": "Test Playlist 0078",
"description": "Update Playlist description",
"public": false,
}
To update the existing resource we use Put/Patch method. Put is used to update the complete existing resource while Patch is used for partial modification.
public void updatePlaylist() {
String playlistId = "your-playlist-id";
RestAssured
.given()
.baseUri("https://api.spotify.com/v1/playlists/" + playlistId)
.contentType(ContentType.JSON)
.header("Authorization", "Bearer your-access-token") // Include your Spotify access token
.body(updatedPlaylistJson)
.when()
.put("/playlists/" + playlistId)
.then()
.statusCode(200);
}
In this example, I have created a method updatePlaylist
that sends a PUT request to update a playlist’s details.
Save the methods and execute the tests. We can refine this code by passing the payload in the form of POJO classes instead of strings.
4)Serialization and Deserialization
The data exchange between the client and server occurs in the form of JSON/XML. Serialization is the process of converting Java objects (whether hashmap or POJO classes) into JSON, while deserialization is the reverse process of mapping JSON to an object.
Rest Assured can use the Jackson/GSON library for the same.
4.1) Create POJO Class
Plain old Java Object (POJO) is a class used for object representation of structured data(JSON). This class contains all the keys as nodes and provides getters and setters to add the abstraction layer.
Why use POJO?
- For better readability
- Reusability
- Easy access to data
- Type Safety
For JSON to POJO conversion, many free online tools are available so you can use those as well as per the requirement. In your project structure create a package let’s say “com. demo. model” and create a class “UserProfile” inside that package and add the following script.
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "country", "display_name", "email", "explicit_content", "external_urls", "followers", "href", "id",
"images", "product", "type", "uri" })
public class UserProfile {
@JsonProperty("country")
private String country;
@JsonProperty("display_name")
private String displayName;
@JsonProperty("email")
private String email;
@JsonProperty("explicit_content")
private ExplicitContent explicitContent;
@JsonProperty("external_urls")
private ExternalUrls externalUrls;
@JsonProperty("followers")
private Followers followers;
@JsonProperty("href")
private String href;
@JsonProperty("id")
private String id;
@JsonProperty("images")
private List<Object> images = null;
@JsonProperty("product")
private String product;
@JsonProperty("type")
private String type;
@JsonProperty("uri")
private String uri;
@JsonProperty("country")
public String getCountry() {
return country;
}
@JsonProperty("country")
public void setCountry(String country) {
this.country = country;
}
@JsonProperty("display_name")
public String getDisplayName() {
return displayName;
}
@JsonProperty("display_name")
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
@JsonProperty("email")
public String getEmail() {
return email;
}
@JsonProperty("email")
public void setEmail(String email) {
this.email = email;
}
@JsonProperty("explicit_content")
public ExplicitContent getExplicitContent() {
return explicitContent;
}
@JsonProperty("explicit_content")
public void setExplicitContent(ExplicitContent explicitContent) {
this.explicitContent = explicitContent;
}
@JsonProperty("external_urls")
public ExternalUrls getExternalUrls() {
return externalUrls;
}
@JsonProperty("external_urls")
public void setExternalUrls(ExternalUrls externalUrls) {
this.externalUrls = externalUrls;
}
@JsonProperty("followers")
public Followers getFollowers() {
return followers;
}
@JsonProperty("followers")
public void setFollowers(Followers followers) {
this.followers = followers;
}
@JsonProperty("href")
public String getHref() {
return href;
}
@JsonProperty("href")
public void setHref(String href) {
this.href = href;
}
@JsonProperty("id")
public String getId() {
return id;
}
@JsonProperty("id")
public void setId(String id) {
this.id = id;
}
@JsonProperty("images")
public List<Object> getImages() {
return images;
}
@JsonProperty("images")
public void setImages(List<Object> images) {
this.images = images;
}
@JsonProperty("product")
public String getProduct() {
return product;
}
@JsonProperty("product")
public void setProduct(String product) {
this.product = product;
}
@JsonProperty("type")
public String getType() {
return type;
}
@JsonProperty("type")
public void setType(String type) {
this.type = type;
}
@JsonProperty("uri")
public String getUri() {
return uri;
}
@JsonProperty("uri")
public void setUri(String uri) {
this.uri = uri;
}
}
As you can see I am using Jackson annotations here because for other requests we are going to use the Single POJO class for serialization and deserialization. If you are unaware of Jackson- annotations and their importance please read here.
To create a playlist using the POST method, generate another POJO class in the model package. Name it as Playlist and include the following code:
package com.spotify.oauth.tests.pojo;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"description",
"id",
"name",
"owner",
"public",
"type",
})
public class Playlist {
@JsonProperty("description")
private String description;
@JsonProperty("id")
private String id;
@JsonProperty("name")
private String name;
@JsonProperty("owner")
private Owner owner;
@JsonProperty("public")
private Boolean _public;
@JsonProperty("type")
private String type;
@JsonProperty("description")
public String getDescription() {
return description;
}
@JsonProperty("description")
public Playlist setDescription(String description) {
this.description = description;
return this;
}
@JsonProperty("id")
public String getId() {
return id;
}
@JsonProperty("id")
public void setId(String id) {
this.id = id;
}
@JsonProperty("name")
public String getName() {
return name;
}
@JsonProperty("name")
public Playlist setName(String name) {
this.name = name;
return this;
}
@JsonProperty("owner")
public Owner getOwner() {
return owner;
}
@JsonProperty("owner")
public void setOwner(Owner owner) {
this.owner = owner;
}
@JsonProperty("public")
public Boolean getPublic() {
return _public;
}
@JsonProperty("public")
public Playlist setPublic(Boolean _public) {
this._public = _public;
return this;
}
@JsonProperty("type")
public String getType() {
return type;
}
@JsonProperty("type")
public void setType(String type) {
this.type = type;
}
}
4.2) Create Reusable methods
- Create a utility package inside the src/test/java folder.
- Create a class named spec builder
- Add the following request and response method in the spec builder class.
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.filter.log.LogDetail;
import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
public class specBuilder {
static String token="";
public static RequestSpecification getRequestSpec(){
return new RequestSpecBuilder().setBaseUri("https://api.spotify.com")
.setBasePath("/v1")
.addHeader("Authorization","Bearer " +token)
.setContentType(ContentType.JSON).log(LogDetail.ALL).build() };
public static ResponseSpecification getResponseSpec(){
return new ResponseSpecBuilder().
log(LogDetail.ALL).
build();
}}
4.3)Update the Test script
Get Query(Fetch User details)
1)Create an object of the UserProfilePOJO class.
2)Extract the response and store it in the object.
3)Assert response payload fields.
4) Save id(user id) in a variable for future reference.
@Test
public void GetUserId(){
UserProfile response = RestAssured.given(specBuilder.getRequestSpec())
.when().get("/me")
.then().spec(specBuilder.getResponseSpec())
.assertThat().statusCode(200)
.extract().as(UserProfile.class);
assertThat(response.getCountry(),equalTo("IN"));
assertThat(response.getDisplayName(),equalTo("does it matter?"));
}
ITestContext interface is used to store and share data across the tests. By Using this interface we can store the value of the key “id” that will be used for API chaining. Update the GetUserId() method with the following code, create a variable and store id, and then set the attribute :
public void GetUserId(ITestContext context){
suite = context.getSuite();
String userId=response.getId();
suite.setAttribute("UserId", userId);
}
Post Method (Create a playlist)
- Add a Test method to create a playlist
- Create an object of the Playlist class and set the name, description, and status using the builder pattern.
- Create another object of Playlist POJO and store the deserialized response as (.class).
- Use spec Builder class reusable method getRequestSpec() and getResponseSpec() to replace common items like baseUri, token etc.
- Perform Assertion.
public void ShouldBeAbleToCreateAPlaylist(ITestContext context) {
suite = context.getSuite();
String userid = (String) suite.getAttribute("UserId");
Playlist requestPlaylist = new Playlist().setName("New Playlist 0078")
.setDescription("New playlistdescription")
.setPublic(true);
Playlist responseplaylist = RestAssured.given(specBuilder.getRequestSpec())
.body(requestPlaylist)
.when().post("users/" + userid + "/playlists")
.then()
.spec(specBuilder.getResponseSpec()).assertThat().statusCode(201)
.extract().response().as(Playlist.class);
assertThat(requestPlaylist.getName(), equalTo(responseplaylist.getName()));
assertThat(requestPlaylist.getDescription(),
equalTo(responseplaylist.getDescription()));
assertThat(requestPlaylist.getPublic(), equalTo(responseplaylist.getPublic()));
String playlistid = responseplaylist.getId();
System.out.println("plyalistid: " + playlistid);
suite.setAttribute("PlaylistId", playlistid);
}
In the attached image we can see that the playlist id is saved using the ITestContext interface and can be used to update the playlist in the next API call.
Update (Update Playlist)
Now I want to update the already created playlist. Refer to the below code where I have used the put method and provided the playlist id as the path param to which I want to update the resource.
public void UpdatePlaylist(ITestContext context)
{
suite = context.getSuite();
String playlistid= (String) suite.getAttribute("PlaylistId");
Playlist requestPlaylist = new Playlist().setName("New Playlist 0078")
.setDescription("Playlist description updated")
.setPublic(true);
Playlist responseplaylist=RestAssured.given(specBuilder.getRequestSpec())
.body(requestPlaylist)
.when().put("/playlists/"+playlistid)
.then()
.spec(specBuilder.getResponseSpec())
.assertThat().statusCode(200)
.extract().response().as(Playlist.class);
assertThat(requestPlaylist.getName(),equalTo(responseplaylist.getName()));
assertThat(requestPlaylist.getDescription(),equalTo(responseplaylist.getDescription()));
assertThat(requestPlaylist.getPublic(),equalTo(responseplaylist.getPublic()));
}
I have added assertions to validate that the resource is updated properly.
In this post, I have provided the Rest Assured framework basic code that can be used to understand the concepts and develop the complete framework.
Use lombok https://projectlombok.org/setup/maven