API Testing
Test API behavior from unit level through contract, integration, security, and end-to-end testing.
API Testing Strategy
API testing should cover multiple levels.
Unit tests verify isolated business logic. Integration tests verify database and dependency interactions. Contract tests verify compatibility between producers and consumers. End-to-end tests verify realistic workflows through the API boundary.
Do not rely entirely on end-to-end tests. They are slower and often harder to diagnose.
A strong suite tests both success paths and failure paths.
Testing HTTP Endpoints
Endpoint Tests
For every important endpoint, test authentication, authorization, valid input, invalid input, missing resources, conflict conditions, pagination boundaries, malformed parameters, and dependency failures.
A test should assert more than status code. Validate response headers, response schema, important fields, side effects, and database state where appropriate.
For destructive operations, verify that unauthorized callers cannot cause the side effect.
const response = await request(app)
.get('/users/42')
.set('Authorization', `Bearer ${token}`)
.expect('Content-Type', /json/)
.expect(200);
expect(response.body.id).toBe('42');Contract Testing
Contract tests verify that an API provider continues to satisfy expectations of its consumers.
This is particularly valuable in microservice architectures where independent teams deploy services at different times.
OpenAPI schemas can also be used to validate requests and responses automatically. Consumer-driven contracts can verify that the provider implements exactly what known consumers depend upon.
- Test negative cases as aggressively as success cases.
- Contract tests protect independently deployed systems.
- Schema validation can catch accidental API changes.
API Security Testing
Security tests should attempt unauthorized access to objects, endpoints, functions, and tenant data.
Test expired tokens, wrong audiences, invalid signatures, missing scopes, role escalation, malformed input, injection attempts, oversized requests, rate-limit behavior, and sensitive-field exposure.
The goal is not merely to prove that valid users can access valid resources. The goal is to prove that invalid users cannot access resources or operations outside their authority.
For every protected resource, include tests proving that another user or tenant cannot access it merely by changing an identifier.