Skip to content

Testing APIs

Tests are what let you change an API with confidence. Three layers, each catching a different class of bug, give good coverage without redundant effort.

flowchart TD
  U[Unit: handlers, validation, pure logic - many, fast] --> I[Integration: real routes end-to-end - fewer]
  I --> C[Contract: responses match the OpenAPI spec - focused]
Many fast unit tests, fewer integration tests, focused contract checks
  • Unit tests — exercise pure pieces: a validator, a pagination helper, an error mapper. Fast and numerous.
  • Integration tests — send real requests through the actual router and assert status, headers, and body. These catch wiring bugs unit tests miss.
  • Contract tests — assert that responses conform to the OpenAPI schema, so the implementation and the published contract never drift.

Frameworks like Hono expose app.request(...), so you can call routes in-process without a network:

JavaScript

For each endpoint, test the happy path and the failure paths: the right status code, the response shape, the error body for invalid input (does it match your problem+json?), auth required where expected, and pagination metadata on collections.

Testing only the happy path Symptom:

  • Tests only send valid requests — resource created, status 201
  • Never test validation errors, auth failures, not-found, or conflicts
  • Test every error path for every status code the API documents

Mocking the database in integration tests Symptom:

  • Integration tests mock the database — the real query never runs
  • Bad SQL or a broken constraint passes in tests but fails in production
  • Use a real test database (in-memory SQLite or a test container)

💡 Real-world examples

Stripe:

  • Test-mode API keys run tests against the real Stripe environment without moving real money
  • Webhook testing via the Stripe CLI: stripe listen --forward-to localhost:3000/webhook

GitHub:

  • No separate sandbox API — teams test against disposable test repos/orgs instead
  • Contract tests against the published OpenAPI spec catch drift in CI before it ships

Sources: Stripe testing, Stripe CLI webhooks, GitHub REST API description

Which test layer sends real requests through the actual router?
What do contract tests specifically guard against?
Why explicitly test failure paths?