Need the #1 custom application developer in Brisbane?Click here →

Testing APIs

9 min read

Your API is a contract between front-end and back-end. Bugs in the API affect every consumer. Testing APIs thoroughly catches regressions before they reach production and waste your users' time.

Manual Testing with Postman

Postman is the standard tool for API testing. Send requests, inspect responses, set headers, manage environments. It's intuitive and powerful.

Create collections of requests (get user, create post, etc.). Organize into folders. Run requests individually or in sequence. Check responses match expectations. This is valuable for development and quick verification.

Automated API Tests

Manual testing doesn't scale. Write automated tests. Make HTTP requests, inspect responses, assert on data. Run tests in CI/CD on every commit. Catch regressions before they reach production.

Jest, Mocha, pytest, and others support API testing. A test might be: POST /api/users, assert 201 response, assert user object has expected fields.

Test Pyramid for APIs

Unit tests: test functions in isolation. Given input, expect output. Fast, numerous. Database is mocked.

Integration tests: test the full stack. API endpoint, database, maybe external services. Slower but closer to reality.

End-to-end tests: test complete flows. User signs up, creates post, likes a comment. The whole application. Slowest but most comprehensive.

The pyramid: many unit tests, fewer integration tests, few end-to-end tests. This provides fast feedback (unit tests) with broader coverage.

What to Test in APIs

Happy path: normal operation. POST /api/users with valid data succeeds. GET /api/users returns users. DELETE /api/users/1 removes the user.

Error cases: invalid input (POST /api/users with no email fails), not found (GET /api/users/999 returns 404), authentication (GET /api/protected without token returns 401).

Authorization: users can access their own data but not others'. Admin endpoints require admin role.

Edge cases: empty arrays, null values, maximum length strings, special characters in inputs.

Contract Testing

Contract tests verify that the API matches what consumers expect. Pact is the main tool. Consumers specify the requests they make and responses they expect. Providers verify they satisfy the contracts.

Contract testing is valuable in microservices. Service A depends on Service B's API. Contract tests ensure they stay in sync without running the full integration.

Mocking External Dependencies

Tests shouldn't call real external services. A test calling Stripe is slow and has side effects (real charges). Mock external services using nock (JavaScript) or responses (Python).

Define mock responses. When your code calls the external service, the mock intercepts and returns the expected response. Tests run fast and have no side effects.

Test Databases

Integration tests need a database. Options: an in-memory database for speed (SQLite in memory), a separate test database, Docker containers.

Isolation is critical. Tests shouldn't affect each other. Run each test in a transaction, roll back after. This ensures clean state.

Running Tests in CI

CI (Continuous Integration) runs tests on every commit. A pull request includes tests. CI runs them. If tests fail, the PR can't be merged. This catches regressions immediately.

In GitHub Actions, GitLab CI, or Jenkins, set up jobs that run: npm test or pytest. Fail the build if tests fail.

Smoke Testing in Staging

Before production, deploy to staging and run automated smoke tests. Basic health checks: API responds, database connection works, critical endpoints function.

Smoke tests catch deployment issues before they hit production. A missing environment variable, a misconfigured dependency—smoke tests catch these.

API Monitoring in Production

Monitor production API health. Response times, error rates, latency. Alert when metrics exceed thresholds.

Services like Datadog, New Relic, or open-source solutions like Prometheus track this. Use them to understand production behavior and catch issues early.

TypeSpeedCoverageSetup EffortBest For
Unit testsVery fastNarrow, implementation levelLowFunction correctness
Integration testsModerateBroader, with real databaseModerateAPI contracts
End-to-end testsSlowComprehensive, full flowHighUser workflows
Contract testsFastAPI compatibilityModerateMicroservices
Smoke testsVery fastBasic healthLowPost-deployment
Tip
Write tests as you develop. Test-driven development (write test, make it pass, refactor) leads to better code. At minimum, write tests for critical functionality and common bugs.
Developer Insight
Don't test implementation details. Test behavior. A test asserting that a function calls a specific internal method is brittle—refactoring breaks it. Test inputs and outputs instead.