Q9 of 37 · API testing
How do you test that an API endpoint returns the correct response?
Short answer
Short answer: Send the request with a known input, assert four things: status code, response shape (schema), key field values, and timing if relevant. Use tools like Postman, REST Assured, or Playwright APIRequestContext. Cover happy path + 1-2 error cases.
Detail
A complete API test asserts on more than just the body — it covers the contract holistically.
The four assertions every API test should make (where applicable):
Status code — the documented one.
expect(res.status).toBe(200). If 201 is documented, 200 is a failure even with the right body.Response shape — does it have the expected fields and types? Either field-by-field (
expect(body.user.id).toBeDefined()) or holistically with a JSON Schema.Key field values — for the inputs you sent, the response is correct.
expect(body.email).toBe('alice@example.com').Headers — when relevant.
Content-Type,Cache-Control,X-Request-IDfor debugging traceability.
A complete example:
test('GET /users/:id returns the user', async ({ request }) => {
const res = await request.get('/users/42');
expect(res.status()).toBe(200);
expect(res.headers()['content-type']).toContain('application/json');
const body = await res.json();
expect(body).toMatchObject({
id: '42',
email: expect.stringMatching(/^.+@.+$/),
role: expect.any(String),
});
});
What to cover beyond the happy path (for any meaningful endpoint):
- 404 — request a non-existent ID.
- 401 — request without an auth token.
- 403 — request as a user without permission.
- 400 — request with invalid input (missing required field, bad type).
- Edge values — boundary cases (empty list, max-length strings, very long IDs).
What junior tests often miss:
- Schema instead of fields — write the schema once and validate every response against it. Misses fewer regressions than ad-hoc field checks.
- Test isolation — each test should set up its own data, not rely on previous tests' results.
- Negative cases — happy path passes 100% of the time, but real bugs are usually in error paths. Cover at least 401 and 400 alongside 200.
The interview signal: writing tests that assert the contract, not just "the call worked."
// EXAMPLE
users.api.test.js
import { test, expect } from '@playwright/test';
test('GET /users/:id', async ({ request }) => {
const res = await request.get('https://api.example.com/users/42', {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
// 1. Status
expect(res.status()).toBe(200);
// 2. Headers
expect(res.headers()['content-type']).toContain('application/json');
// 3. Shape and 4. Values
const body = await res.json();
expect(body).toMatchObject({
id: '42',
email: expect.stringMatching(/@/),
active: true,
});
});