Skip to content

API Testing with Jest

API testing verifies that your HTTP endpoints behave correctly: right status codes, correct response shapes, and proper handling of authentication and errors. This lesson covers strategies that apply beyond any single framework.

What to Verify for Every Endpoint

A thorough API test checks more than "it returns 200". At minimum, verify the status code, the response body's shape (using .toMatchObject() or .toHaveProperty() rather than a fragile full-equality check), and any relevant headers (like Content-Type).

Beyond the happy path, test authentication requirements, input validation errors, and not-found scenarios — these are exactly the cases most likely to have bugs in real production APIs.

test('GET /users/:id returns the requested user', async () => {
  const response = await request(app).get('/users/1');

  expect(response.status).toBe(200);
  expect(response.headers['content-type']).toMatch(/json/);
  expect(response.body).toMatchObject({ id: 1, name: expect.any(String) });
});

Checking status, content type, and a partial body shape together gives solid coverage without an overly brittle exact-match assertion.

Testing Authentication Requirements

test('returns 401 without a valid token', async () => {
  const response = await request(app).get('/profile');
  expect(response.status).toBe(401);
});

test('returns 200 with a valid token', async () => {
  const response = await request(app)
    .get('/profile')
    .set('Authorization', `Bearer ${validTestToken}`);
  expect(response.status).toBe(200);
});
  • Always test both an unauthenticated request and an authenticated one for protected routes.
  • Use a test-specific token (signed with a test secret, or a mocked auth middleware) rather than a real production credential.
  • Test expired or malformed tokens separately from missing tokens — they often hit different code paths.
  • Avoid hardcoding real secrets in test files; use environment variables or test fixtures.

API Testing Checklist

What a thorough API test suite should cover per endpoint.

Scenario Expected Behavior
Valid request Correct status code and response shape
Missing required field 400 with a descriptive error message
Unauthenticated request 401 for protected routes
Insufficient permissions 403 for authenticated but unauthorized users
Resource not found 404 with a clear error
Unexpected server error 500 without leaking internal details
Response content type Matches expected format (e.g. application/json)

Validating Response Schemas

For APIs with many fields or nested structures, consider a schema validation library (like Zod or Ajv with JSON Schema) to assert the entire response shape at once, rather than manually listing every field with .toHaveProperty().

const userSchema = z.object({ id: z.number(), name: z.string(), email: z.string().email() });

test('response matches the expected user schema', async () => {
  const response = await request(app).get('/users/1');
  expect(() => userSchema.parse(response.body)).not.toThrow();
});

Testing Pagination and Query Parameters

For list endpoints, test that pagination parameters (?page=2&limit=10) and filters (?status=active) actually affect the returned data, not just that the endpoint returns *something*.

test('returns only active users when filtered', async () => {
  const response = await request(app).get('/users?status=active');

  expect(response.body.every((u) => u.status === 'active')).toBe(true);
});

Common Mistakes

  • Only testing the happy path and skipping validation, auth, and not-found scenarios.
  • Using full .toEqual() on a response body that includes volatile fields like timestamps or IDs.
  • Hardcoding real credentials or secrets directly in API test files.
  • Not verifying that filters or pagination parameters actually change the returned data.

Key Takeaways

  • A thorough API test checks status code, response shape, and relevant headers together.
  • Test authentication and authorization for every protected endpoint, not just the happy path.
  • Schema validation libraries can simplify asserting on large or complex response shapes.
  • Always verify that filters and pagination parameters genuinely affect the response.

Pro Tip

For every new endpoint, write down the list of scenarios (valid, missing field, unauthenticated, not found, server error) before writing any test code — treating it like a checklist catches gaps that ad hoc testing tends to miss.