How to Assert JSON in Rest Assured Using Hamcrest

Introduction

In API testing, validating responses is crucial to ensure the data returned meets our expectations. One of the most common response formats in APIs is JSON, which can represent complex nested data structures. Rest Assured, a Java library for testing RESTful web services, makes it simple to validate JSON data through assertions. When combined with Hamcrest matchers, Rest Assured provides a powerful way to write expressive, readable assertions for JSON responses.

In this article, we'll dive into how to use Hamcrest matchers with Rest Assured to assert JSON data in various scenarios.


Prerequisites

To follow along, make sure you have:

  • Java installed on your machine.
  • Rest Assured and Hamcrest dependencies added to your project (if using Maven, add them in pom.xml).

Here's an example of the required dependencies:

<dependency>

    <groupId>io.rest-assured</groupId>

    <artifactId>rest-assured</artifactId>

    <version>5.3.0</version>

    <scope>test</scope>

</dependency>

<dependency>

    <groupId>org.hamcrest</groupId>

    <artifactId>hamcrest</artifactId>

    <version>2.2</version>

</dependency>

Getting Started with JSON Assertion in Rest Assured

In Rest Assured, JSON assertions can be made using response.then() combined with body() or rootPath(), and you can use Hamcrest matchers for additional validation options. Let’s look at some examples.


Example Setup

Consider a sample API endpoint https://api.example.com/users/1 which returns the following JSON:

{

  "id": 1,

  "name": "John Doe",

  "email": "johndoe@example.com",

  "address": {

    "city": "New York",

    "zipcode": "10001"

  },

  "roles": ["user", "admin"]

}

1. Simple Assertion of JSON Fields

To assert simple fields in JSON, such as id and name, we can use equalTo matcher in combination with body().

import static io.restassured.RestAssured.*;

import static org.hamcrest.Matchers.*;


public class SimpleAssertionTest {

    public void testUserDetails() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .body("id", equalTo(1))

                .body("name", equalTo("John Doe"))

                .body("email", equalTo("johndoe@example.com"));

    }

}

Here, equalTo is a Hamcrest matcher that verifies the id, name, and email fields have expected values.


2. Nested JSON Assertions

For nested JSON structures, you can specify the path using a dot notation to target inner fields. For example, to assert the city and zipcode inside the address object:

public class NestedAssertionTest {

    public void testUserAddress() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .body("address.city", equalTo("New York"))

                .body("address.zipcode", equalTo("10001"));

    }

}

Using "address.city" accesses the city inside the address object, allowing us to verify the data of nested fields.


3. Array Assertions

If your JSON contains an array, you can validate its elements with hasItems() or contains(). For instance, to check the roles user and admin:

public class ArrayAssertionTest {

    public void testUserRoles() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .body("roles", hasItems("user", "admin"));

    }

}

Here, hasItems ensures that both user and admin are present in the roles array.


4. Advanced Assertions with Multiple Matchers

You can chain multiple Hamcrest matchers for advanced validation. For instance, you can verify the name starts with "John" and email contains "example.com".

public class AdvancedAssertionTest {

    public void testUserDetailsAdvanced() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .body("name", startsWith("John"))

                .body("email", containsString("example.com"));

    }

}

In this case, startsWith and containsString provide flexible assertions for partial matching.


5. Using Root Path for Repetitive Paths

If you have multiple assertions for fields within the same parent node, rootPath() can simplify your code by setting a default base path. Here’s an example:

public class RootPathExample {

    public void testUserWithRootPath() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .rootPath("address")

                .body("city", equalTo("New York"))

                .body("zipcode", equalTo("10001"))

                .noRootPath(); // Reset the root path if needed for future assertions

    }

}

With rootPath("address"), all subsequent assertions assume a base path of address.


6. Combining Assertions in a Single body Statement

You can combine multiple assertions within a single body method call to keep your code concise.

public class CombinedAssertionTest {

    public void testCombinedAssertions() {

        given()

            .when()

                .get("https://api.example.com/users/1")

            .then()

                .statusCode(200)

                .body("id", equalTo(1),

                      "name", equalTo("John Doe"),

                      "email", equalTo("johndoe@example.com"),

                      "address.city", equalTo("New York"),

                      "roles", hasItems("user", "admin"));

    }

}

This approach keeps assertions organized, especially useful for multiple fields within the same JSON response.

Followers