Q2 of 37 · API testing
How do you validate a JSON response schema in your API tests?
Short answer
Short answer: Define the contract once as a JSON Schema, then validate every response against it inside the test. Tools like Ajv, JSV (REST Assured), or pytest-jsonschema fail the test when the response shape drifts — even if the values are correct.
Detail
Field-by-field assertions (expect(body.user.id).toBeTruthy()) catch the values you remembered to check but miss the ones you didn't. Schema validation flips that: you assert the entire response shape — types, required fields, formats, allowed values — in one statement. If the API silently renames user_id to userId, the schema check fails immediately.
The standard tool is JSON Schema (Draft 7 / 2020-12). You write a schema describing the expected response (or import the OpenAPI definition the backend already publishes) and validate the body against it. Most ecosystems have a fast validator: Ajv for Node/JS, REST Assured + JsonSchemaValidator for Java, jsonschema for Python.
There are two failure modes worth knowing in interviews:
- The schema is too loose (
additionalProperties: true, norequiredlist). You'll miss missing fields. Always setadditionalProperties: falsefor the responses you own and list every required key. - The schema drifts from the OpenAPI source of truth. If the API team owns an OpenAPI spec, derive your test schemas from it (or run contract tests with Pact/Spectral) so a backend change auto-fails the consumer suite.
Schema validation is cheap, runs in milliseconds, and catches an entire class of regressions that field-by-field assertions can't.
// EXAMPLE
user.api.test.ts
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const userSchema = {
type: 'object',
required: ['id', 'email', 'createdAt'],
additionalProperties: false,
properties: {
id: { type: 'string', format: 'uuid' },
email: { type: 'string', format: 'email' },
name: { type: 'string', maxLength: 120 },
role: { type: 'string', enum: ['admin', 'manager', 'viewer'] },
createdAt: { type: 'string', format: 'date-time' },
},
} as const;
const validate = ajv.compile(userSchema);
test('GET /users/:id matches schema', async () => {
const res = await fetch('https://api.example.com/users/42');
const body = await res.json();
expect(res.status).toBe(200);
const ok = validate(body);
if (!ok) {
throw new Error(
'Schema validation failed: ' + ajv.errorsText(validate.errors),
);
}
});// WHAT INTERVIEWERS LOOK FOR
// COMMON PITFALL
// Related questions
What is contract testing (e.g. Pact) and when do you use it?
API testing
How would you architect an API test suite from scratch for a microservices team?
API testing
How would you justify investment in contract testing to a leadership team focused on velocity?
API testing
How does Karate handle JSON schema validation differently from REST Assured?
Karate