When testing web services, especially APIs, having a robust testing framework is essential. REST Assured is a popular Java-based library that simplifies API testing for developers. It allows for smooth integration with various testing tools and frameworks, making it a go-to solution for developers and testers working on API testing.
It covers advanced REST Assured concepts such as authentication mechanisms, schema validation, XML handling, and automation of complex API workflows. These topics are essential for anyone aiming to deepen their understanding of REST Assured and effectively automate testing for real-world APIs.
Understanding Authentication Mechanisms
When testing APIs, authentication is often a critical part of interacting with secured endpoints. REST Assured simplifies working with various authentication strategies, including basic authentication, token-based authentication, OAuth 2.0, and custom headers/cookies.
Basic Authentication
Basic Authentication is one of the simplest forms of authentication, requiring a username and password to access the API. In REST Assured, it can be handled easily with the auth().basic() method.
- Open Postman and go to your collection.
- Click the three dots (
...) next to the collection name and select Edit. - Navigate to the Authorization tab in the collection settings.
- Choose the Type of authorization from the dropdown. Common options include:

Example:
package io.learn.auth;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
public class BasicAuthTest {
@Test
public void testBasicAuth() {
given()
.auth()
.basic("postman", "password")
.baseUri("https://postman-echo.com")
.when()
.get("/basic-auth")
.then()
.statusCode(200);
}
@Test
public void testBasicAuthNegative() {
given()
.baseUri("https://postman-echo.com")
.when()
.get("/basic-auth")
.then()
.statusCode(401);
}
}
Output:

This sends a request with the specified credentials and verifies the response status code.
Token-Based Authentication
Many modern APIs use token-based authentication, where a bearer token is included in the request header. This method is commonly used with APIs like those from third-party services.
Example:
package io.learn.auth;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.equalTo;
public class FootballAPITests {
@BeforeClass
public void setUp() {
RestAssured.baseURI = "https://api.football-data.org";
RestAssured.basePath = "/v4";
RestAssured.requestSpecification = new RequestSpecBuilder()
.setContentType("application/json")
.addHeader("X-Auth-Token", "XXXXXXXXXXXXXXXX") // use your API Key
.build();
}
@Test
public void testCase1() {
get("/teams")
.then().statusCode(200);
}
@Test
public void testCase2() {
get("/teams/66")
.then().statusCode(200)
.body("name", equalTo("Manchester United FC"));
}
}
Output:


Here, the Authorization header includes the bearer token, which authenticates the user for accessing protected endpoints.
OAuth 2.0 Authentication
OAuth 2.0 is an advanced and secure authentication mechanism that is widely used by services like GitHub and Google. It requires an access token that is passed using the auth().oauth2() method in REST Assured.
Example:
given()
.auth().oauth2("<token>")
.when()
.get("/user/repos")
.then()
.statusCode(200);
package io.learn.auth;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
public class GitHubAPITests {
private RequestSpecification requestSpecification = new RequestSpecBuilder()
.setBaseUri("https://api.github.com")
.setContentType("application/json")
.build();
@Test
public void testCase1() {
given().
spec(requestSpecification)
.when()
.get("/users/rakesh-vardan/repos")
.then()
.statusCode(200);
}
@Test
public void testCompleteFlow() {
String userName = "rakesh-vardan";
String repoName = "my-dummy-repo-test";
String payLoad = "{\"name\":\""+repoName+"\",\"description\":\"This is a new repository\",\"homepage\":\"https://github.com\",\"private\":false,\"is_template\":false}";
String oAuth2Token = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // use your access token
// create a new repo
given().
spec(requestSpecification)
.body(payLoad)
.auth()
.oauth2(oAuth2Token)
.log().all()
.when()
.post("/user/repos")
.then()
.statusCode(201)
.log().all();
// delete that repo
given().
spec(requestSpecification)
.auth()
.oauth2(oAuth2Token)
.log().all()
.when()
.delete("/repos/"+userName+"/" + repoName)
.then()
.statusCode(204)
.log().all();
}
}
Output:

This method ensures secure access by handling OAuth 2.0 tokens dynamically in git-hub it will create a dynamic repo using the Token bases authentication.
Custom Headers and Cookies
For custom authentication, REST Assured allows developers to inject custom headers and cookies dynamically.
Example:
given()
.header("Custom-Header", "value")
.cookie("SessionID", "12345")
.when()
.get("/endpoint")
.then()
.statusCode(200);
In this example, custom headers and cookies are passed along with the request to simulate user authentication.
2. Schema Validation
Validating the response schema ensures that the API's output follows a predefined structure. This is especially important for APIs that deal with complex data structures or are expected to return consistent formats across different requests.
JSON Schema Validation
REST Assured integrates with the JSON Schema Validator library to perform schema validation. By defining the expected structure of the response (usually in a .json file), you can ensure the response matches the required format.
Steps for Schema Validation:
- Define the Expected Schema
Example schema for a pet API:
package schemavalidation;
import static io.restassured.RestAssured.get;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
import java.util.HashMap;
import java.util.Map;
import org.hamcrest.Matcher;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
public class SchemaValidationTests {
@BeforeClass
public void setUp() {
RestAssured.baseURI = "https://petstore.swagger.io";
RestAssured.basePath = "/v2";
RestAssured.requestSpecification = new RequestSpecBuilder()
.setContentType("application/json").build();
}
@Test
public void testSchemaForPet() {
Map<String, Object> petData = getPetData();
// create a new pet - POST
String newPetID = given().body(petData).when().post("/pet").path("id").toString();
System.out.println("New Pet created is: " + newPetID);
// verify the new pet - GET
get("/pet/" + newPetID).then().statusCode(200)
.body("status", equalTo("pending"))
.body(matchesJsonSchemaInClasspath("PetSchema.json"));
}
private Matcher<?> matchesJsonSchemaInClasspath(String string) {
// TODO Auto-generated method stub
return null;
}
private Map<String, Object> getPetData() {
Map<String, Object> categoryMap = new HashMap<>();
categoryMap.put("id", 1);
categoryMap.put("name", "dog");
Map<String, Object> petMap = new HashMap<>();
petMap.put("name", "doggie");
petMap.put("status", "pending");
petMap.put("category", categoryMap);
return petMap;
}
}
PetSchema.json
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$ref": "#/definitions/Welcome5",
"definitions": {
"Welcome5": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer"
},
"category": {
"$ref": "#/definitions/Category"
},
"name": {
"type": "string"
},
"photoUrls": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"$ref": "#/definitions/Category"
}
},
"status": {
"type": "string"
}
},
"required": [
"category",
"id",
"name",
"photoUrls",
"status",
"tags"
],
"title": "Welcome5"
},
"Category": {
"type": "object",
"additionalProperties": false,
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"name"
],
"title": "Category"
}
}
}
Output:

Add the Dependency
Add the JSON Schema Validator dependency in your pom.xml:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>5.3.0</version>
</dependency>
Validate the Schema
Example validation code:
given()
.baseUri("https://petstore.swagger.io/")
.when()
.get("/pet/1")
.then()
.assertThat()
.body(matchesJsonSchemaInClasspath("pet-schema.json"));
- This checks if the response body matches the schema defined in the
pet-schema.jsonfile.
3. Handling XML Responses
While JSON is the most commonly used format for APIs, XML is still prevalent in many legacy systems and certain industries. REST Assured allows developers to handle XML responses efficiently.
Validating XML Responses
To handle XML responses, you need to set the Accept header to application/xml to inform the server that XML is expected in the response.
Example:
package xml;
import org.testng.annotations.Test;
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;
public class XMLTests {
@Test
public void testJSONData() {
given()
.baseUri("https://petstore.swagger.io/v2")
.when()
.get("/pet/9223372036854760000")
.then()
.statusCode(200);
}
@Test
public void testXMLData() {
given()
.baseUri("https://petstore.swagger.io/v2")
.accept("application/xml")
.when()
.get("/pet/9223372036854760000")
.then()
.statusCode(200)
.body("Pet.status", equalTo("pending"));
}
}
Output:

This checks that the status element in the XML response matches "available". "24.28"
4. Automating Complex API Flows
Real-world API testing often involves more than just sending a single request. It may involve creating resources, verifying them, and then deleting them, all within a single test flow.
Example: Automating GitHub API Flows
Creating a Repository
To create a repository on GitHub using the API, you can use the POST method with a JSON payload.
Example:
String payload = "{ \"name\": \"test-repo\", \"private\": false }";
given()
.auth().oauth2("<token>")
.body(payload)
.when()
.post("/user/repos")
.then()
.statusCode(201);
This creates a repository with the specified name.
Deleting a Repository
To delete the repository, you can use the DELETE method.
Example:
given()
.auth().oauth2("<token>")
.when()
.delete("/repos/<username>/test-repo")
.then()
.statusCode(204);
This deletes the repository and ensures no further data exists for the specified repository.
Combining the Flow
You can now combine both actions into a flow and assert the outcomes of each operation. This is useful for automating the lifecycle of resources in API testing.
5. Best Practices for REST Assured
Use Request Specifications
Centralizing common configurations like base URIs, authentication, headers, and other settings makes your tests more maintainable and easier to read.
Example:
RequestSpecification spec = new RequestSpecBuilder()
.setBaseUri("https://api.github.com/")
.setAuth(oauth2("<token>"))
.build();
Leverage Logs
Logging requests and responses helps in debugging and understanding test failures. You can log everything in your requests and responses to get more insight into the interaction.
Example:
given()
.log().all()
.when()
.get("/endpoint")
.then()
.log().body();
Dynamic Data Handling
API tests often require dynamic data such as user tokens or IDs that may change with each test run. Using variables for dynamic data helps ensure that your tests are adaptable to different test scenarios.