API Testing
Testing the layer under the UI — where the logic actually lives, and where contract breaks are cheapest to catch.
Below the Interface
A thorough API test checks more than the happy path body. Status code, response shape, headers, error cases, authorisation, and what happens on a repeat call all matter — and each of them has shipped broken in production somewhere this week.
| Check | Typical miss |
|---|---|
| Status codes | 200 with an error body inside it |
| Response schema | A field silently renamed or nulled |
| Authorisation | Another user's id works fine |
| Validation | Bad input accepted, or a 500 instead of a 400 |
| Idempotency | A retried POST charges twice |
| Pagination | Page 2 repeats items from page 1 |
Contract Testing
Two services agree on a shape. One team changes it. Nothing fails until production, because each service's own tests pass against its own idea of the contract. Contract testing closes that gap without standing up both systems together.
| Side | What it verifies |
|---|---|
| Consumer | Records the requests it makes and the responses it needs |
| Provider | Replays those expectations against the real implementation |
| Broker | Shares contracts between the two, versioned per deploy |
The payoff is that a provider learns it is about to break a consumer before deploying, in a fast test rather than an integration environment. It is the cheapest defence against the failure mode microservices are most prone to.
Postman & REST Clients
Exploring an API by hand is how you learn what it really does. A GUI client is right for discovery; a scripted runner is right for anything you intend to repeat.
| Tool | Best at |
|---|---|
| Postman / Insomnia | Exploration, collections, sharing with the team |
| curl / HTTPie | One-off checks and pasting into a bug report |
| REST Client files in the repo | Requests versioned alongside the code |
| Code-level clients | The real suite — assertions, fixtures, CI |
# a bug report with this in it is reproducible by anyone
curl -i -X POST https://api.example.com/v1/orders \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"sku":"ABC-1","qty":0}'
# expected 400 with {"error":"qty must be positive"} — actual 500
Interview Questions
Why test at the API layer?
It is far faster and more stable than the UI and much broader than unit tests. Most business logic is reachable there without a browser, so it gives the best information per second of runtime.
What should an API test assert beyond the body?
Status code, response schema, headers, error cases, authorisation, idempotency on retry and pagination behaviour — each is a common production defect.
What problem does contract testing solve?
Two services agreeing on a shape that one of them changes. Each side's own tests keep passing, and it only breaks in production. Contract tests verify the agreement without running both systems together.
How does consumer-driven contract testing work?
The consumer records the requests it makes and the responses it needs; the provider replays those expectations against its real implementation, so it learns it is about to break someone before deploying.
Why validate responses against a schema?
It catches silently renamed or nulled fields and keeps the published API definition honest — the docs failing the build rather than quietly going stale.
Is a Postman collection a test suite?
It is good for exploration and sharing, but it drifts from the code, lives outside version control and is hard to review. Checks that matter belong in the repository, running on every commit.