REST Assured Response Validation: Body, JsonPath & Fields
Sending a request is the easy part; the value of an API test is in what you validate. This guide covers everything you check in a REST Assured response — status, headers, single and multiple fields, arrays, nested objects, searches inside arrays, extracted values and response time — plus how to keep those checks reusable.
For JSON basics and JSON vs XML, see JSON & XML in API Testing.
The Validation Order
Status code → headers (Content-Type) → body fields → arrays and nested data → schema → response time
Check the status first: if it's wrong, the body assertions only add noise.
Interview answer: "I validate the status code first, then headers like Content-Type, then body fields with then().body() and Hamcrest matchers — single fields, arrays and nested objects using GPath — plus a JSON schema for the whole contract and a response-time check for critical APIs."
Sample Response
All examples use this response from GET /users/101:
{
"id": 101,
"name": "John",
"email": "john@test.com",
"status": "ACTIVE",
"roles": ["Admin", "Tester", "Manager"],
"address": { "city": "Hyderabad", "zip": "500001" },
"orders": [
{ "id": 1, "item": "Laptop", "amount": 55000 },
{ "id": 2, "item": "Mouse", "amount": 800 },
{ "id": 3, "item": "Keyboard", "amount": 2500 }
]
}
Status and Headers
given()
.when()
.get("/users/101")
.then()
.statusCode(200)
.contentType(ContentType.JSON)
.header("Cache-Control", containsString("no-cache"));
Single and Multiple Fields
.then()
.statusCode(200)
.body("id", equalTo(101))
.body("name", equalTo("John"))
.body("email", endsWith("@test.com"))
.body("status", equalTo("ACTIVE"));
// Several fields in one body() call
.body("id", equalTo(101), "name", equalTo("John"), "status", equalTo("ACTIVE"));
The path strings ("name", "address.city") use GPath, REST Assured's JsonPath syntax.
If one assertion fails, REST Assured still reports every failed body() check in the same then() block.
Nested Objects
.body("address.city", equalTo("Hyderabad"))
.body("address.zip", matchesPattern("\\d{6}"))
.body("address", hasKey("city"));
Arrays
.body("roles", hasItem("Tester"))
.body("roles", hasItems("Admin", "Tester"))
.body("roles", hasSize(3))
.body("roles", not(hasItem("Guest")))
.body("roles[0]", equalTo("Admin")) // index access
.body("orders.size()", equalTo(3))
.body("orders.item", contains("Laptop", "Mouse", "Keyboard")) // all items, in order
.body("orders.amount", everyItem(greaterThan(0)));
orders.item collects the item field from every element into a list — a quick way to check a whole column.
Searching Inside Arrays (GPath find / findAll) ⭐
When you don't know an element's position, search for it:
// The item of the order with id 2
.body("orders.find { it.id == 2 }.item", equalTo("Mouse"))
// Items of all orders over 2,000
.body("orders.findAll { it.amount > 2000 }.item", hasItems("Laptop", "Keyboard"))
// Highest amount
.body("orders.max { it.amount }.item", equalTo("Laptop"))
// Sum of all amounts
.body("orders.collect { it.amount }.sum()", equalTo(58300));
These are Groovy expressions evaluated by REST Assured — far more robust than orders[1].item when the order of elements can change.
Extracting Values
Response response = given().when().get("/users/101").then().statusCode(200).extract().response();
String city = response.jsonPath().getString("address.city");
int orderCount = response.jsonPath().getList("orders").size();
List<String> items = response.jsonPath().getList("orders.item");
Map<String, Object> firstOrder = response.jsonPath().getMap("orders[0]");
// Or extract one value directly — typical for API chaining
int newId = given().body(user).when().post("/users").then().statusCode(201).extract().path("id");
Extract when the next request needs the value (tokens, IDs) or when you need logic that's clumsy in matchers. Otherwise, assert inside then() — it gives better failure messages.
Common Hamcrest Matchers
| Matcher | Checks |
|---|---|
equalTo(x) |
Exact value |
notNullValue() / nullValue() |
Present and not null / null |
containsString(), startsWith(), endsWith() |
Text |
matchesPattern(regex) |
Format (IDs, codes, dates) |
greaterThan(), lessThan(), closeTo(x, delta) |
Numbers |
hasItem(), hasItems(), hasSize(), empty() |
Collections |
contains(...) / containsInAnyOrder(...) |
Exact list, ordered / unordered |
everyItem(matcher) |
Every element |
hasKey() |
Object has a field |
anyOf(), allOf(), not() |
Combining conditions |
Response Time
.then()
.statusCode(200)
.time(lessThan(2000L)); // milliseconds
Treat this as a smoke-level guard against very slow responses. A single timing in a functional test is noisy — measure real performance with dedicated load tests: API Performance & Load Testing.
Reusable Checks: ResponseSpecification
ResponseSpecification ok = new ResponseSpecBuilder()
.expectStatusCode(200)
.expectContentType(ContentType.JSON)
.expectResponseTime(lessThan(3000L))
.build();
given().when().get("/users/101").then().spec(ok).body("name", equalTo("John"));
Combine it with a RequestSpecification (base URI, headers, auth) in your framework's base class.
Add .log().ifValidationFails() so failed tests print the full request and response.
More: REST Assured Framework Design.
Best Practices
- Assert the exact status code first.
- Validate static values exactly and dynamic ones (IDs, timestamps) by type or format.
- Search arrays with
find/findAllinstead of relying on index positions. - Add a JSON schema for the full contract; keep field assertions for business values.
- Test error responses too — their body and shape matter to clients.
- Log only on failure to keep CI output readable.
See how requests are built and responses validated step by step in the REST Assured Visualizer.
From Real Projects
My API experience covers CRUD operations, HTTP methods, JSON path, and validating JSON and XML responses, and my automation work was in Java with TestNG. That combination is exactly what REST Assured builds on: HTTP calls in Java, JSON path for extracting values, and TestNG for running and grouping the tests. If you already know Selenium with TestNG, REST Assured fits into the same project structure. Testsigma supports web, mobile and API test automation, so understanding APIs was part of understanding the product I was testing. Validate the status, headers and body in the same test.
📚 Official documentation: REST Assured official site · MDN: HTTP
Frequently Asked Questions
How do you validate the response body in REST Assured?
With then().body(path, matcher) — a GPath expression plus a Hamcrest matcher such as equalTo(), hasItem() or hasSize().
How do you validate a nested field?
Use dot notation:
.body("address.city", equalTo("Hyderabad"))
How do you find an element in an array without knowing its index?
GPath:
.body("orders.find { it.id == 2 }.item", equalTo("Mouse"))
Or use findAll for several matches.
How do you extract a value for the next request?
Use:
.extract().path("id")
Or:
.extract().response().jsonPath().getString("token")
How do you validate response time?
Use:
.time(lessThan(2000L))
This is useful as a guard; proper performance testing needs dedicated load tests.
What is a ResponseSpecification?
A reusable set of expectations (status, content type, time) applied with .spec(), so common checks live in one place.