Rest Assured Response Validation and Advanced Features: The Complete Guide
By Chandan Singh | Category: API Testing | Reading time: ~15 minutes
Sending a request is the easy part of API automation. The real value of a test is in validating the response correctly: status, headers, body, schema, data types and performance. In this guide you will learn the most important Rest Assured features for response validation, with ready-to-use Java examples you can copy into your framework today.
All examples use static imports and the free practice API https://jsonplaceholder.typicode.com. Add these dependencies (rest-assured, json-schema-validator, jackson-databind, testng) in your pom.xml:
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import io.restassured.path.json.JsonPath;
import io.restassured.response.Response;
1. Hamcrest Matchers in Rest Assured
Hamcrest provides readable matchers used inside body(). They make assertions look like English and give clear failure messages.
given().baseUri("https://jsonplaceholder.typicode.com")
.when().get("/users/1")
.then().statusCode(200)
.body("name", equalTo("Leanne Graham"))
.body("email", containsString("@"))
.body("address.city", not(emptyOrNullString()))
.body("id", greaterThan(0));
Popular matchers: equalTo, containsString, hasItem, hasItems, hasSize, everyItem, greaterThan, notNullValue, anyOf, allOf.
2. JSONPath in Rest Assured
Rest Assured uses Groovy’s GPath syntax to navigate JSON. Use dot notation for objects and index or filters for arrays: address.geo.lat, [0].name, findAll{it.id > 5}.
3. Extracting Values Using JSONPath
Extract data to reuse in later requests (for example a token or an ID).
Response res = get("https://jsonplaceholder.typicode.com/users/1");
String name = res.jsonPath().getString("name");
int id = res.path("id");
String city = JsonPath.from(res.asString()).getString("address.city");
System.out.println(name + " lives in " + city);
.extract().path("id") at the end of a then() chain to validate and extract in one flow.4. Validating Nested JSON Responses
Use dot notation to reach any depth. No need to parse objects manually.
get("/users/1").then()
.body("address.street", notNullValue())
.body("address.geo.lat", notNullValue())
.body("company.name", equalTo("Romaguera-Crona"));
5. Validating JSON Arrays and Collections
get("/users").then()
.body("$", hasSize(10))
.body("name", hasItem("Ervin Howell"))
.body("id", everyItem(greaterThan(0)))
.body("findAll { it.id < 4 }.username", hasItems("Bret", "Antonette"))
.body("[0].address.city", equalTo("Gwenborough"));
Use find for the first match, findAll for all matches, and sum(), max(), min() for aggregates.
6. JSON Schema Validation
Schema validation checks the structure: required fields, types and formats. It catches breaking API changes that value checks miss. Save the schema in src/test/resources/user-schema.json.
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
get("/users/1").then()
.assertThat()
.body(matchesJsonSchemaInClasspath("user-schema.json"));
7. XML Response Validation and XPath
For SOAP or legacy REST services, use xmlPath() or XPath expressions.
given().when().get("/books.xml").then()
.body("catalog.book[0].title", equalTo("Rest Assured Basics"))
.body(hasXPath("count(//book)", equalTo("5")))
.body(hasXPath("//book[@id='b1']/price", equalTo("29.99")));
String title = get("/books.xml").xmlPath().getString("catalog.book[0].title");
8. Serialization and Deserialization in Java
Serialization converts a Java object into JSON/XML (request body). Deserialization converts JSON/XML back into a Java object (response). Rest Assured does both automatically when Jackson or Gson is on the classpath.
| Process | Direction | Rest Assured usage |
|---|---|---|
| Serialization | Java object → JSON | .body(userObject) |
| Deserialization | JSON → Java object | .as(User.class) |
9. Using POJO Classes in Rest Assured
A POJO (Plain Old Java Object) mirrors your JSON. It makes tests type-safe and easy to maintain.
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private int id;
private String name;
private String email;
// constructors, getters and setters
}
10. Sending Requests Using Java Objects
User user = new User(0, "Chandan", "chandan@example.com");
given().baseUri("https://jsonplaceholder.typicode.com")
.contentType("application/json")
.body(user) // serialized automatically
.when().post("/users")
.then().statusCode(201)
.body("name", equalTo("Chandan"));
11. Converting Responses into Java Objects
User user = get("/users/1").as(User.class);
Assert.assertEquals(user.getId(), 1);
// For lists
User[] users = get("/users").as(User[].class);
List<User> list = get("/users").jsonPath().getList("", User.class);
12. ObjectMapper with Jackson
Use Jackson’s ObjectMapper when you need full control: custom naming, date formats or ignoring unknown fields.
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
String json = mapper.writeValueAsString(user); // object to JSON
User parsed = mapper.readValue(json, User.class); // JSON to object
User fromResponse = mapper.readValue(get("/users/1").asString(), User.class);
13. Reusable Request Specifications
RequestSpecification stores common settings (base URI, headers, auth) so you write them once.
public static RequestSpecification reqSpec = new RequestSpecBuilder()
.setBaseUri("https://jsonplaceholder.typicode.com")
.setContentType(ContentType.JSON)
.addHeader("Authorization", "Bearer " + token)
.log(LogDetail.URI)
.build();
given().spec(reqSpec).when().get("/users/1").then().statusCode(200);
14. Reusable Response Specifications
public static ResponseSpecification okSpec = new ResponseSpecBuilder()
.expectStatusCode(200)
.expectContentType(ContentType.JSON)
.expectResponseTime(lessThan(2000L))
.build();
given().spec(reqSpec).when().get("/users").then().spec(okSpec);
BaseTest or SpecFactory class so every test class reuses them.15. Logging Requests and Responses
given().log().all() // request: headers, body, params
.when().get("/users/1")
.then().log().ifValidationFails() // log response only on failure
.statusCode(200);
// Write logs to a file
PrintStream ps = new PrintStream(new FileOutputStream("api.log"));
RestAssured.config = RestAssuredConfig.config()
.logConfig(LogConfig.logConfig().defaultStream(ps));
Use ifValidationFails() in CI to keep reports clean while still capturing evidence for failures. Mask sensitive headers with blacklistHeader("Authorization").
16. Conditional Assertions and Custom Validation
Sometimes the expected result depends on the response itself. Extract first, then assert using TestNG or AssertJ.
Response res = get("/users/1");
String status = res.jsonPath().getString("status");
if ("ACTIVE".equals(status)) {
Assert.assertNotNull(res.path("lastLogin"));
} else {
Assert.assertNull(res.path("lastLogin"));
}
// Soft assertions: report all failures together
SoftAssert soft = new SoftAssert();
soft.assertEquals(res.statusCode(), 200);
soft.assertTrue(res.time() < 2000);
soft.assertAll();
17. Custom Matchers and Assertion Utilities
Create your own Hamcrest matcher for rules you repeat often, such as “valid email”.
public class IsValidEmail extends TypeSafeMatcher<String> {
protected boolean matchesSafely(String s) {
return s.matches("^[\\w.+-]+@[\\w-]+\\.[\\w.]+$");
}
public void describeTo(Description d) { d.appendText("a valid email address"); }
public static Matcher<String> validEmail() { return new IsValidEmail(); }
}
get("/users/1").then().body("email", IsValidEmail.validEmail());
18. Handling Dynamic JSON Responses
IDs, timestamps and tokens change on every call. Do not hardcode them: validate the type and pattern instead, or deserialize into a Map.
get("/orders/latest").then()
.body("orderId", matchesPattern("^ORD-\\d{6}$"))
.body("createdAt", notNullValue());
Map<String, Object> map = get("/orders/latest").as(new TypeRef<Map<String, Object>>() {});
Assert.assertTrue(map.containsKey("orderId"));
19. Handling Date, Time and Time Zone Validations
Always compare dates as objects, never as raw strings, and normalise to UTC.
String ts = get("/orders/1").path("createdAt"); // 2026-10-26T08:30:00Z
Instant created = Instant.parse(ts);
Assert.assertTrue(created.isBefore(Instant.now()));
ZonedDateTime ist = created.atZone(ZoneId.of("Asia/Kolkata"));
System.out.println(ist); // 2026-10-26T14:00+05:30
Tolerate small clock differences with Duration.between(a, b).abs().getSeconds() < 5.
20. Working with Large API Responses
- Validate selectively: use JSONPath filters instead of converting the whole body to objects.
- Use pagination: loop through
?page=n&limit=100instead of fetching everything at once. - Turn off body logging:
log().all()on a 20 MB response slows your suite. - Stream when needed:
res.asInputStream()and Jackson’s streaming API reduce memory use. - Set timeouts: configure
HttpClientConfigto avoid hanging tests.
int total = get("/users").jsonPath().getList("findAll { it.id > 5 }").size();
Frequently Asked Questions
What is the difference between JSONPath and Hamcrest in Rest Assured?
JSONPath locates data in the response; Hamcrest verifies it. You normally use them together: .body("name", equalTo("Leanne")).
Should I use POJOs or JSONPath?
Use JSONPath for quick checks and POJOs for complex payloads and reusable test data.
How do I validate that the API structure has not changed?
Use JSON Schema validation as a contract check in every regression run.
Which JSON library is best, Jackson or Gson?
Both work. Jackson is more widely used, faster and better supported in Rest Assured projects.
Conclusion
Strong API tests validate more than the status code. Combine Hamcrest, JSONPath, schema validation, POJOs and reusable specifications to build a framework that is readable, stable and easy to scale. Next, read our related guides on Rest Assured basics and API testing interview questions, and share your questions in the comments below.
No comments:
Post a Comment