Introduction

In our previous post, we explored Authentication in REST Assured, learning how to secure API requests. Now, we’ll dive into Response Validation, a critical aspect of API testing that ensures APIs return the expected data and behave correctly. This guide is designed for beginners and experienced developers, providing clear explanations and practical examples to master response validation in REST Assured.

Key Point: Response validation in REST Assured allows you to verify the status code, headers, and body of API responses to ensure they meet expected criteria.

What is Response Validation?

Response Validation involves checking the HTTP response from an API to confirm it matches the expected outcome. This includes validating:

  • Status Code: Ensures the request was processed correctly (e.g., 200 OK, 404 Not Found).
  • Headers: Verifies response headers like Content-Type or custom headers.
  • Body: Checks the content of the response, such as JSON or XML fields, for correctness.

REST Assured provides a powerful Given-When-Then syntax and Hamcrest matchers to validate responses efficiently.

Validating Responses in REST Assured

Let’s explore how to validate API responses using REST Assured, with examples using the public API https://jsonplaceholder.typicode.com.

Option 1: Validating Status Code

The simplest validation checks the HTTP status code to ensure the request was successful.


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

public class StatusCodeValidationTest {

    @Test
    public void testStatusCode() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .when()
                .get("/posts/1")
            .then()
                .statusCode(200);
    }
}

Explanation:

  • statusCode(200): Verifies the response has a 200 OK status, indicating success.
  • Use other status codes like 404 (Not Found) or 500 (Server Error) based on the test scenario.
Important: Always validate the status code first, as it indicates whether the request was processed correctly before checking the response body or headers.

Option 2: Validating Response Body (JSON)

Validate specific fields in a JSON response using Hamcrest matchers.


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

public class JsonBodyValidationTest {

    @Test
    public void testJsonBody() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .queryParam("userId", 1)
            .when()
                .get("/posts")
            .then()
                .statusCode(200)
                .body("[0].userId", equalTo(1))
                .body("[0].title", notNullValue())
                .body("size()", greaterThan(0));
    }
}

Explanation:

  • body("[0].userId", equalTo(1)): Verifies the first post’s userId is 1.
  • body("[0].title", notNullValue()): Ensures the first post’s title exists.
  • body("size()", greaterThan(0)): Confirms the response array contains at least one item.
  • Use dot notation (e.g., [0].field) to navigate JSON structures.

Option 3: Validating Response Headers

Validate response headers to ensure they match expected values, such as Content-Type.


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

public class HeaderValidationTest {

    @Test
    public void testHeaders() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .when()
                .get("/posts/1")
            .then()
                .statusCode(200)
                .header("Content-Type", containsString("application/json"))
                .header("Server", notNullValue());
    }
}

Explanation:

  • header("Content-Type", containsString("application/json")): Verifies the response is JSON.
  • header("Server", notNullValue()): Ensures the Server header exists.

Option 4: Using Request Specification for Consistent Validation

Use a Request Specification to apply common settings, and validate responses consistently.


import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class ValidationWithSpecTest {

    private static RequestSpecification requestSpec;

    @BeforeAll
    public static void setup() {
        requestSpec = new RequestSpecBuilder()
            .setBaseUri("https://jsonplaceholder.typicode.com")
            .addHeader("Accept", "application/json")
            .build();
    }

    @Test
    public void testValidationWithSpec() {
        given()
            .spec(requestSpec)
            .pathParam("postId", 1)
            .when()
                .get("/posts/{postId}")
            .then()
                .statusCode(200)
                .body("id", equalTo(1))
                .body("title", notNullValue())
                .header("Content-Type", containsString("application/json"));
    }
}

Explanation:

  • RequestSpecBuilder: Sets the base URI and Accept header.
  • spec(requestSpec): Applies the specification to the test.
  • The test validates status code, JSON body, and headers.
Pro Tip: Combine Request Specifications with response validation to enforce consistent checks across tests, improving maintainability.

Step 1: Extracting and Validating Response Data

Extract the response for advanced validation or processing.


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

public class ExtractResponseTest {

    @Test
    public void testExtractAndValidate() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        Response response = given()
            .when()
                .get("/users/1")
            .then()
                .statusCode(200)
                .extract().response();

        String name = response.path("name");
        String email = response.path("email");

        // Custom validation
        assert name != null && !name.isEmpty();
        assert email.contains("@");

        System.out.println("User Name: " + name + ", Email: " + email);
    }
}

Explanation:

  • extract().response(): Extracts the full response.
  • response.path(): Retrieves specific fields for custom validation.
  • This approach is useful for complex validations or logging.

Step 2: Validating Complex JSON Structures

Validate nested JSON structures or arrays using dot notation or Hamcrest matchers.


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

public class ComplexJsonValidationTest {

    @Test
    public void testComplexJson() {
        RestAssured.baseURI = "https://jsonplaceholder.typicode.com";

        given()
            .when()
                .get("/users/1")
            .then()
                .statusCode(200)
                .body("address.street", notNullValue())
                .body("address.geo.lat", instanceOf(String.class))
                .body("company.name", containsStringIgnoringCase("inc"));
    }
}

Explanation:

  • body("address.street", notNullValue()): Validates a nested field in the JSON response.
  • body("address.geo.lat", instanceOf(String.class)): Checks the type of a nested field.
  • body("company.name", containsStringIgnoringCase("inc")): Verifies a substring in a field, case-insensitive.

Step 3: Verify Setup with pom.xml

Ensure your pom.xml includes dependencies for REST Assured and JUnit:


<dependencies>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest</artifactId>
        <version>2.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Run the tests using mvn test or your IDE’s test runner to confirm the setup.

Tips for Beginners

  • Start with Status Codes: Always validate the status code before checking headers or body.
  • Use Hamcrest Matchers: Leverage matchers like equalTo, notNullValue, or containsString for flexible validation.
  • Check API Documentation: Understand the expected response structure and fields.
  • Enable Logging: Use RestAssured.enableLoggingOfRequestAndResponseIfValidationFails() to debug validation failures.
Troubleshooting Tip: If validations fail, check the response structure using logging or tools like Postman. Ensure your JSON paths and expected values match the API’s response.

What’s Next?

In the next post, we’ll cover Data-Driven Testing, exploring how to run REST Assured tests with multiple data sets for comprehensive API testing. Stay tuned for more hands-on examples!