Testing REST APIs
1. Writing Unit Tests
| Scope | Tools |
|---|---|
| Controller logic (mock services) | JUnit + Mockito, pytest, Jest |
| Validation rules | Bean Validation tests |
| Serialization/mapping | Jackson tests, MapStruct verification |
| Pure functions | Fast, no I/O |
2. Writing Integration Tests
Example: Spring MockMvc Integration Test
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIT {
@Autowired MockMvc mvc;
@Test
void createUser_returns201() throws Exception {
mvc.perform(post("/users")
.contentType("application/json")
.content("{\"name\":\"Alice\",\"email\":\"a@x.com\"}"))
.andExpect(status().isCreated())
.andExpect(header().exists("Location"))
.andExpect(jsonPath("$.email").value("a@x.com"));
}
}
3. Testing Authentication and Authorization
| Test Case | Expected |
|---|---|
| No token | 401 |
| Invalid/expired token | 401 |
| Valid token, wrong role | 403 |
| Valid token, correct role | 2xx |
| Token for different user accessing private resource | 403/404 |
4. Testing Error Handling
| Scenario | Verify |
|---|---|
| Invalid JSON body | 400 + error structure |
| Missing required field | 422 + field-level errors |
| Resource not found | 404 |
| Database failure | 500 + no stack trace leak |
5. Testing Rate Limiting Behavior
Example: Rate Limit Test
@Test
void rateLimitReturns429AfterThreshold() {
for (int i = 0; i < 100; i++) {
api.get("/users").assertStatus(200);
}
api.get("/users")
.assertStatus(429)
.assertHeader("Retry-After")
.assertHeader("X-RateLimit-Remaining", "0");
}
6. Testing Pagination and Filtering
| Test | Verify |
|---|---|
| Default page size | Returns N items |
| page=2 | Returns next slice, no overlap |
| Empty page | 200 + empty array (not 404) |
| Filter combinations | AND logic, correct subset |
| Invalid filter field | 400 |
7. Using API Testing Tools
| Tool | Strength |
|---|---|
| Postman / Bruno | Manual testing, collections |
| REST Assured | Java DSL for API tests |
| Supertest | Node.js HTTP assertions |
| HTTPie / curl | CLI exploration |
| Schemathesis | Property-based from OpenAPI |
| Karate | BDD API tests |
8. Implementing Contract Testing
| Tool | Approach |
|---|---|
| Pact | Consumer-driven contracts |
| Spring Cloud Contract | Producer-driven |
| OpenAPI + Schemathesis | Spec-as-contract testing |
| Goal | Detect breaking changes before deploy |
9. Testing Idempotency Behavior
| Test | Expected |
|---|---|
| POST + Idempotency-Key, called twice | Same response, single side effect |
| Same key, different body | 422 |
| PUT same body twice | Same final state |
| DELETE twice | 204 + 404 (or 204+204) |
10. Load Testing
| Tool | Use |
|---|---|
| k6 | JS scripts, modern UX |
| JMeter | GUI, mature |
| Gatling | Scala DSL, high throughput |
| Locust | Python, distributed |
| wrk / hey | Lightweight CLI |
11. Testing CORS Configuration
| Test | Verify Header |
|---|---|
| OPTIONS preflight | Allow-Origin, Allow-Methods, Allow-Headers |
| Disallowed origin | No Allow-Origin → browser blocks |
| Credentials request | Allow-Credentials: true + specific origin |
12. Implementing Automated API Tests
| Stage | Tests Run |
|---|---|
| Pre-commit | Lint, unit tests |
| PR build | Unit + integration + contract |
| Pre-deploy | E2E smoke tests |
| Post-deploy | Synthetic monitoring, canary |
| Nightly | Load tests, security scans |