Testing RESTful APIs with REST-Assured

Bubu Tripathy
10 min readMay 9, 2023

--

Introduction

In the world of software development, testing plays a crucial role in ensuring the quality and reliability of applications. When it comes to testing APIs, a popular and powerful tool in the Java ecosystem is REST-Assured. REST-Assured is a Java library that provides a simple and intuitive syntax for testing RESTful APIs.

REST-Assured allows developers to write expressive and readable test cases for API endpoints, enabling comprehensive validation of various aspects such as response status codes, headers, JSON payloads, and more. With its fluent API and extensive set of features, REST-Assured simplifies the process of testing and validating RESTful APIs.

In this tutorial, we will explore different aspects of testing RESTful APIs using REST-Assured.

Basic Example

To use REST-Assured in your Maven project, you need to include the following dependencies in your project’s pom.xml file:

<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>json-schema-validator</artifactId>
<version>4.4.0</version>
<scope>test</scope>
</dependency>

Let’s start with a basic example to perform a GET request and validate the response status code:

import org.testng.annotations.Test;
import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testGetRequest() {
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200);
}
}

In the above example, we use the given() method to set up the request, the when() method to specify the HTTP method and URL, and the then() method to assert the response.

To check if a specific JSON key has a value, you can use the Matchers class from the Hamcrest library. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testJSONKeyHasValue() {
given()
.when()
.get("https://api.example.com/user/123")
.then()
.body("name", equalTo("John Doe"));
}
}

In the above example, we assert that the JSON response should contain a key named “name” with a value equal to “John Doe”.

To check if a JSON array contains a specific value, you can use the hasItem matcher. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testJSONArrayHasValue() {
given()
.when()
.get("https://api.example.com/users")
.then()
.body("name", hasItem("John Doe"));
}
}

Sometimes, you may need to validate the entire JSON response against a JSON schema. REST-Assured provides support for JSON schema validation using the JSON Schema Validator module. Here’s an example:

import static io.restassured.RestAssured.*;
import static io.restassured.module.jsv.JsonSchemaValidator.*;

public class APITest {

@Test
public void testJSONSchemaValidation() {
given()
.when()
.get("https://api.example.com/users")
.then()
.body(matchesJsonSchemaInClasspath("user-schema.json"));
}
}

In the above example, we assert that the JSON response should match the schema defined in the “user-schema.json” file.

Note: You need to provide the JSON schema file in the classpath of your project.

When dealing with floating-point numbers in JSON responses, precision can be an issue. To handle this, REST-Assured provides a closeTo matcher to compare float and double values with a specific delta. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testFloatComparison() {
given()
.when()
.get("https://api.example.com/price")
.then()
.body("price", closeTo(19.99, 0.01));
}
}

In the above example, we assert that the “price” value in the JSON response should be approximately equal to 19.99 with a delta of 0.01.

By default, REST-Assured uses the GET method for API requests. However, you can explicitly specify different HTTP methods using the request() method. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testExplicitRequestMethod() {
given()
.request("PUT")
.when()
.urlEncodingEnabled(false)
.body("name=John Doe")
.post("https://api.example.com/user/123")
.then()
.statusCode(200);
}
}

In the above example, we explicitly specify the PUT method for the request and send a POST request to update a user’s information.

To avoid repeating the base URL in every request, you can set a base URI using the baseURI method. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@BeforeClass
public void setUp() {
baseURI = "https://api.example.com";
}

@Test
public void testGetRequest() {
given()
.when()
.get("/users")
.then()
.statusCode(200);
}
}

In the above example, we set the base URI as “https://api.example.com" in the setUp() method using the baseURI variable. Then, in the test case, we only provide the relative path ("/users") for the GET request.

You can measure the response time of an API request using REST-Assured. Here’s an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testResponseTime() {
given()
.when()
.get("https://api.example.com/users")
.then()
.time(lessThan(2000L)); // Response time should be less than 2000 milliseconds
}
}

In the above example, we assert that the response time of the API request should be less than 2000 milliseconds.

REST Assured provides built-in support for logging request details, which can be helpful for debugging and troubleshooting. Here’s an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testLoggingRequestDetails() {
given()
.log().all()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200);
}
}

In the above example, we enable request logging using the `log().all()` method. This will print out the complete request details, including headers, parameters, and body, to the console.

HTTP POST Request

To make an HTTP POST request using REST-Assured, you can use the given() method to set up the request, the when() method to specify the HTTP method and URL, and the then() method to validate the response. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testPostRequest() {
given()
.contentType("application/json")
.body("{\"name\": \"John Doe\", \"age\": 30}")
.when()
.post("https://api.example.com/users")
.then()
.statusCode(201)
.body("id", notNullValue());
}
}

In the above example, we set the content type to JSON using the contentType() method and provide the request body as a JSON string using the body() method. Then, we send a POST request to the specified URL ("/users") using the post() method. Finally, we validate that the response has a status code of 201 (Created) and the "id" field is not null.

Validating the Response

REST-Assured provides various methods to validate different aspects of the response, such as status codes, headers, and response bodies. Here are some common validation methods:

  • statusCode(int code): Validates the status code of the response.
  • header(String name, String value): Validates the value of a specific header.
  • body(String path, Matcher<?> matcher): Validates a specific value in the response body using Hamcrest matchers.

Here’s an example that demonstrates these validation methods:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testPostRequestValidation() {
given()
.contentType("application/json")
.body("{\"name\": \"John Doe\", \"age\": 30}")
.when()
.post("https://api.example.com/users")
.then()
.statusCode(201)
.header("Content-Type", "application/json")
.body("id", notNullValue())
.body("name", equalTo("John Doe"))
.body("age", greaterThan(18));
}
}

By combining these validation methods, you can thoroughly test the response of your POST requests and ensure that the API behaves as expected.

Handling Different Status Codes

In addition to asserting a specific status code, you may need to handle different status codes and perform different actions accordingly. REST-Assured allows you to handle different status codes using the assertThat() method from Hamcrest matchers. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testStatusCodeHandling() {
given()
.when()
.get("https://api.example.com/user/123")
.then()
.statusCode(
anyOf(
is(200), // Success
is(404), // Not Found
is(500) // Server Error
)
);
}
}

In the above example, we retrieve user data for the specified user ID (“/user/123”). We handle different status codes by asserting that the response status code should be either 200 (OK), 404 (Not Found), or 500 (Server Error). This allows you to handle specific scenarios based on different HTTP response codes.

Chaining Status Code Validation

REST-Assured allows you to chain multiple status code validations using the and() method. This can be useful when you need to validate multiple status codes in a single test case. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testMultipleStatusCodes() {
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.and()
.statusCode(not(404));
}
}

In the above example, we validate that the response status code should be 200 (OK) and also that it should not be 404 (Not Found). Chaining status code validations allows you to perform multiple assertions on the response status code within a single test case.

Query String Parameters

In many cases, RESTful APIs require query string parameters to provide additional information or filter results. To include query string parameters in an HTTP GET request using REST-Assured, you can use the queryParam() method. This method allows you to specify the key-value pairs of the query parameters. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testGetRequestWithQueryParams() {
given()
.queryParam("param1", "value1")
.queryParam("param2", "value2")
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200);
}
}

To verify that the query string parameters are correctly included in the request, you can log the request details using the log().all() method. Here's an updated example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testGetRequestWithQueryParams() {
given()
.queryParam("param1", "value1")
.queryParam("param2", "value2")
.log().all()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200);
}
}

HTTP Headers

When testing RESTful APIs, it is often necessary to retrieve and verify specific HTTP header values in the API response. To retrieve a single HTTP header value from the response, REST-Assured provides the header() method. This method allows you to specify the header name and retrieve its value. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testRetrieveSingleHeaderValue() {
Response response =
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.extract()
.response();

String contentType = response.header("Content-Type");
System.out.println("Content-Type: " + contentType);
}
}

If you need to retrieve multiple HTTP header values from the response, you can use the headers() method. This method returns a Headers object that allows you to retrieve and iterate over all the headers. Here's an example:

import static io.restassured.RestAssured.*;
import io.restassured.http.Headers;
import io.restassured.response.Response;

public class APITest {

@Test
public void testRetrieveMultipleHeaderValues() {
Response response =
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.extract()
.response();

Headers headers = response.headers();
for (Header header : headers) {
System.out.println(header.getName() + ": " + header.getValue());
}
}
}

Retrieving the Entire HTTP Response Body

When testing RESTful web service endpoints, it is often necessary to retrieve the entire HTTP response body for further analysis and verification. To retrieve the entire HTTP response body using REST-Assured, you can utilize the extract().response() method. This method allows you to extract the complete response, including the body, from the API request. Here's an example:

import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testRetrieveResponseBody() {
Response response =
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.extract()
.response();

String responseBody = response.getBody().asString();
System.out.println("Response Body: " + responseBody);
}
}

REST-Assured provides various methods to retrieve the response body in different data types based on your requirements. Some common methods include:

  • getBody().asString(): Retrieves the response body as a string.
  • getBody().asByteArray(): Retrieves the response body as a byte array.
  • getBody().asInputStream(): Retrieves the response body as an input stream.
  • getBody().as(Object.class): Retrieves the response body and deserializes it into a custom object using a supported serialization framework (e.g., Gson, Jackson).

Here’s an example that demonstrates retrieving the response body as different data types:

import static io.restassured.RestAssured.*;
import io.restassured.response.ResponseBody;

public class APITest {

@Test
public void testRetrieveResponseBody() {
Response response =
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.extract()
.response();

ResponseBody<?> responseBody = response.getBody();

String asString = responseBody.asString();
byte[] asByteArray = responseBody.asByteArray();
InputStream asInputStream = responseBody.asInputStream();

// Example using a custom object with Gson
CustomObject customObject = responseBody.as(CustomObject.class);
}
}

Evaluating JSON Content in the Response Body

When testing RESTful APIs, it is common to receive JSON content in the response body. Evaluating the JSON content allows you to verify specific values, structures, or nested objects within the response. REST-Assured provides powerful methods to evaluate JSON content in the response body. The body() method allows you to specify JSON paths and matchers to validate specific values. Here's an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testEvaluateJSONContent() {
given()
.when()
.get("https://api.example.com/user/123")
.then()
.statusCode(200)
.body("name", equalTo("John Doe"))
.body("age", greaterThan(18));
}
}

When dealing with nested JSON objects, REST-Assured provides dot notation to navigate through the JSON structure. Here’s an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testNestedJSONObject() {
given()
.when()
.get("https://api.example.com/user/123")
.then()
.statusCode(200)
.body("address.city", equalTo("New York"))
.body("address.zipCode", startsWith("1"));
}
}

To evaluate JSON arrays in the response body, REST-Assured provides matchers specific to arrays. Here’s an example:

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class APITest {

@Test
public void testJSONArray() {
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200)
.body("books", hasSize(3))
.body("books[0].title", equalTo("Book 1"))
.body("books[1].author", containsString("Doe"));
}
}

JUnit 5 and REST-Assured

If you have a Spring Boot project and want to incorporate JUnit 5 and REST-Assured for testing your APIs, you can follow these steps to set up the necessary dependencies and configurations.

In your project’s pom.xml file, add the following dependencies to include JUnit 5 and REST-Assured:

<dependencies>
<!-- Spring Boot Test Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>

<!-- JUnit 5 Vintage Engine (for backward compatibility with JUnit 4) -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>

<!-- REST Assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

These dependencies include the necessary libraries for Spring Boot testing, JUnit 5, and REST-Assured.

To configure JUnit 5 in your Spring Boot project, create a new file named TestSuite.java (or any suitable name) in the src/test/java directory. Add the following code to enable JUnit 5 support:

import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class TestSuite {

@Test
@DisplayName("Sample Test")
public void sampleTest() {
// Add your test logic here
Assertions.assertTrue(true);
}
}

Now you can write REST-Assured tests to test your APIs. Create a new test class in the src/test/java directory, and start writing your API test cases using REST-Assured. Here's an example:

import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;

public class APITest {

@Test
public void testGetRequest() {
given()
.when()
.get("https://api.example.com/users")
.then()
.statusCode(200);
}
}

You can now run your JUnit 5 tests. Use your preferred IDE or build tool to execute the tests. The tests will be automatically detected and executed.

Conclusion

Testing RESTful APIs is an essential part of ensuring the reliability, functionality, and performance of your applications. REST Assured, a powerful Java library, provides a straightforward and expressive way to write API tests.

By leveraging REST Assured’s fluent API and extensive set of features, you can create comprehensive and readable test cases that verify the behavior and correctness of your APIs. Whether you need to assert response status codes, validate JSON structures, or measure response times, REST Assured provides intuitive methods and matchers to make the testing process efficient and effective.

Thank you for your attention! Happy Learning!

--

--