Response Validation & Assertions
Master response validation techniques — check status codes, JSON body fields, array contents, nested objects, and data types.
Thorough Response Validation
The core of API testing is validating that responses match expectations. Beyond just checking status codes, you need to verify JSON structure, field values, data types, array contents, and nested objects.
Levels of Validation
Good API tests validate at multiple levels: the status code confirms the operation succeeded, the headers confirm the response format, and the body confirms the actual data. Each level catches different types of bugs.
Validating Data Types
Ensure fields are the correct type — numbers are numbers, strings are strings, arrays are arrays. A common bug is an API returning a number as a string or a null instead of an empty array.
Nested Object Validation
Real-world APIs return complex nested structures. You need to navigate into nested objects and arrays to validate deeply nested fields without excessive verbosity.
import { strict as assert } from 'assert';
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
const user = await response.json();
// Status code validation
assert.equal(response.status, 200);
// Field existence
assert.ok('id' in user, 'Should have id');
assert.ok('name' in user, 'Should have name');
assert.ok('email' in user, 'Should have email');
assert.ok('address' in user, 'Should have address');
// Data type validation
assert.equal(typeof user.id, 'number');
assert.equal(typeof user.name, 'string');
assert.equal(typeof user.email, 'string');
assert.equal(typeof user.address, 'object');
// Nested object validation
assert.ok('street' in user.address);
assert.ok('city' in user.address);
assert.ok('geo' in user.address);
assert.equal(typeof user.address.geo.lat, 'string');
assert.equal(typeof user.address.geo.lng, 'string');
// Company nested object
assert.equal(typeof user.company, 'object');
assert.ok(user.company.name.length > 0);
// Array validation
const postsResp = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await postsResp.json();
assert.ok(Array.isArray(posts));
assert.ok(posts.length > 0);
// Validate every item in array has required fields
posts.forEach((post, i) => {
assert.ok(Number.isInteger(post.id), `Post ${i} id should be integer`);
assert.ok(post.title.length > 0, `Post ${i} should have title`);
});
// Find specific item in array
const post5 = posts.find(p => p.id === 5);
assert.ok(post5, 'Post with id 5 should exist');
console.log('All validations passed!');
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
import io.restassured.response.Response;
import org.testng.annotations.Test;
import org.testng.Assert;
import java.util.List;
import java.util.Map;
public class ResponseValidationTest {
private static final String BASE = "https://jsonplaceholder.typicode.com";
@Test
public void testFieldValidation() {
given().baseUri(BASE)
.when().get("/users/1")
.then()
.statusCode(200)
// Field existence + values
.body("id", equalTo(1))
.body("name", notNullValue())
.body("email", notNullValue())
// Data type checks via Hamcrest
.body("id", instanceOf(Integer.class))
.body("name", instanceOf(String.class))
// Nested object validation
.body("address.street", notNullValue())
.body("address.city", notNullValue())
.body("address.geo.lat", notNullValue())
.body("address.geo.lng", notNullValue())
// Company nested object
.body("company.name", not(emptyString()));
}
@Test
public void testArrayValidation() {
given().baseUri(BASE)
.when().get("/posts")
.then()
.statusCode(200)
.body("size()", equalTo(100))
// Every item has required fields
.body("id", everyItem(notNullValue()))
.body("title", everyItem(not(emptyString())))
.body("userId", everyItem(notNullValue()))
// Find specific item
.body("find { it.id == 5 }.title", notNullValue());
}
@Test
public void testExtractAndAssert() {
Response resp = given().baseUri(BASE)
.when().get("/users/1")
.then().extract().response();
// Extract as Map for complex assertions
Map<String, Object> address = resp.jsonPath().getMap("address");
Assert.assertTrue(address.containsKey("street"));
Assert.assertTrue(address.containsKey("city"));
// Extract list
List<String> keys = resp.jsonPath().getList("address.keySet()");
System.out.println("Address fields: " + keys);
}
}
import requests
BASE_URL = 'https://jsonplaceholder.typicode.com'
def test_field_validation():
"""Validate fields, types, and nested objects"""
response = requests.get(f'{BASE_URL}/users/1')
user = response.json()
# Field existence
required_fields = ['id', 'name', 'email', 'address', 'company']
for field in required_fields:
assert field in user, f'Missing field: {field}'
# Data type validation
assert isinstance(user['id'], int)
assert isinstance(user['name'], str)
assert isinstance(user['email'], str)
assert isinstance(user['address'], dict)
# Nested object validation
assert 'street' in user['address']
assert 'city' in user['address']
assert 'geo' in user['address']
assert isinstance(user['address']['geo']['lat'], str)
assert isinstance(user['address']['geo']['lng'], str)
# Company nested object
assert isinstance(user['company'], dict)
assert len(user['company']['name']) > 0
def test_array_validation():
"""Validate arrays and list items"""
response = requests.get(f'{BASE_URL}/posts')
posts = response.json()
assert isinstance(posts, list)
assert len(posts) > 0
# Every item has required fields
for i, post in enumerate(posts):
assert isinstance(post['id'], int), f'Post {i} id should be int'
assert len(post['title']) > 0, f'Post {i} should have title'
assert 'userId' in post, f'Post {i} missing userId'
# Find specific item
post5 = next((p for p in posts if p['id'] == 5), None)
assert post5 is not None, 'Post with id 5 should exist'
def test_negative_response():
"""Validate error responses"""
response = requests.get(f'{BASE_URL}/posts/999999')
assert response.status_code == 404
assert response.json() == {}
Written by PV
© 2026 All Rights Reserved