Beyond 200 OK: What Senior QA Engineers Actually Validate in API Testing
Your API returned 200 OK. Your test passed. But the response contained wrong data, missing fields, incorrect calculations, and stale cached results. A 200 status code is not a successful test — it is the beginning of one.
Contents
Real Bugs Hidden Behind 200 OK
- Wrong data in body: API returns user ID 42’s data when you requested user ID 43
- Missing fields: The
emailfield is gone after a backend refactor — but status is still 200 - Incorrect calculations: Order total shows $100 instead of $90 after a 10% discount
- Stale cached responses: You updated the product price, but the API returns the old price from cache
- Wrong data types: Price returns as string “29.99” instead of number 29.99
The Senior QA Validation Checklist
- Status code — yes, check it, but it is layer 1 of 7
- Response body structure — all expected fields present, no extra fields leaked
- Data types — numbers are numbers, dates are ISO format, booleans are booleans
- Business logic — calculations correct, state transitions valid, relationships consistent
- Schema validation — response matches the API contract (JSON Schema)
- Security — no sensitive data in response, auth tokens required, rate limiting active
- Performance — response time within SLA, no N+1 query indicators
Code: Comprehensive API Validation
test('validate user API response comprehensively', async ({ request }) => {
const response = await request.get('/api/users/1');
// Layer 1: Status
expect(response.status()).toBe(200);
// Layer 2: Structure
const user = await response.json();
expect(user).toHaveProperty('id');
expect(user).toHaveProperty('name');
expect(user).toHaveProperty('email');
expect(user).not.toHaveProperty('password'); // Security: no leaked fields
// Layer 3: Data types
expect(typeof user.id).toBe('number');
expect(typeof user.name).toBe('string');
expect(user.email).toMatch(/^[^@]+@[^@]+\.[^@]+$/);
// Layer 4: Business logic
expect(user.id).toBe(1);
expect(user.name.length).toBeGreaterThan(0);
// Layer 5: Performance
const timing = response.headers()['x-response-time'];
if (timing) expect(parseInt(timing)).toBeLessThan(500);
});
JSON Schema Validation
import Ajv from 'ajv';
const userSchema = {
type: 'object',
required: ['id', 'name', 'email', 'createdAt'],
properties: {
id: { type: 'number' },
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
createdAt: { type: 'string', format: 'date-time' },
},
additionalProperties: false // Reject unexpected fields
};
test('response matches JSON schema', async ({ request }) => {
const response = await request.get('/api/users/1');
const data = await response.json();
const ajv = new Ajv({ allErrors: true });
const validate = ajv.compile(userSchema);
const valid = validate(data);
expect(valid).toBe(true);
if (!valid) console.log(validate.errors);
});
