Q2 of 37 · API testing

How do you validate a JSON response schema in your API tests?

API testingMidapijson-schemavalidationcontract-testing

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:

  1. The schema is too loose (additionalProperties: true, no required list). You'll miss missing fields. Always set additionalProperties: false for the responses you own and list every required key.
  2. 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

Awareness of JSON Schema as the contract, knowledge of additionalProperties: false, and the link to OpenAPI / contract testing as the source of truth.

// COMMON PITFALL

Writing a permissive schema with no required fields and no additionalProperties guard, which validates almost any response. Or duplicating field-by-field assertions instead of trusting the schema.