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.