Q25 of 37 · API testing
How would you architect an API test suite from scratch for a microservices team?
Short answer
Short answer: Layer the strategy: per-service unit + integration tests owned by service teams, cross-service contract tests (Pact), a thin layer of E2E covering business-critical journeys. Standardise tooling, fixtures, auth, and CI patterns. Each service tests its own contract; the platform team owns the orchestration and the journey-level coverage.
Detail
Microservices testing fails when one team tries to test everything end-to-end. The right architecture distributes responsibility — each service owns its own contracts; the platform layer owns the journeys.
The four-layer model:
Layer 1 — Per-service tests (owned by each service team):
- Unit tests for handlers and business logic.
- Integration tests against a real database, hitting the service via HTTP.
- Schema validation against the service's OpenAPI / GraphQL schema.
- Stay fast; run in the service's own CI on every commit.
Layer 2 — Contract tests (cross-service):
- Consumer-driven with Pact, or provider-driven with OpenAPI as the contract.
- Each consumer publishes its expectations; each provider verifies them in CI.
- This catches the bulk of cross-service breakage without running multi-service environments.
Layer 3 — Selective E2E (owned by platform / QA platform):
- 10-30 tests covering business-critical journeys: signup → onboard → buy.
- Run against a real staging environment with all services up.
- Optimised for "is the platform healthy?" not for line-level coverage.
- Run on merge to main or before deploy, not on every PR.
Layer 4 — Synthetic monitors (production):
- Subset of L3 journeys running every 5 minutes against prod.
- Page on failure; backstop the test pyramid.
Standardisation that's worth it:
- Tooling — pick one HTTP test library per language, one schema validator, one mock server. Fragmentation across services kills shared learning.
- Auth helper — every test needs to issue a token. One library; every service uses it.
- Test data — a shared "create test user / org / tenant" helper across services.
- CI patterns — same Jenkinsfile / GitHub Actions template for unit-build-contract-test, parameterised by service.
- Reporting — Allure or equivalent, aggregated across services.
What to avoid:
- One mega E2E suite that tests every service's flow end-to-end. Breaks when any service is down; nobody owns; flake destroys trust.
- Running other services' integration tests in your CI. Service boundaries should match test boundaries.
- Custom auth in each service's test setup. Inconsistent token expiry, scopes, and error handling.
The platform team's job: maintain the standards, the shared tooling, and the L3/L4 journeys. Service teams own everything else. Without this division, either everything centralises (bottleneck) or nothing centralises (chaos).
Migration if you inherit a mess:
- Inventory existing tests by layer. Which are unit, which are E2E, which are duplicates?
- Adopt one shared library (auth, fixtures) and migrate services one at a time.
- Stand up Pact or OpenAPI contract verification for the most-coupled service pair.
- Trim E2E down to journeys, not "every endpoint of every service."
- Document the layered model and run a workshop with service owners.
The interview signal: thinking about who owns what, not just "what tests exist." Microservices testing is a distributed-responsibility problem first, a tooling problem second.