API Testing: Unit, Integration, and Contract Tests
Reliable APIs require a well-thought-out testing strategy that combines unit tests, integration tests, and contract tests to catch issues early and protect interface contracts.
Overview
API testing encompasses all testing activities that ensure an API’s correctness, security, performance, and stability. Unit tests verify isolated functions, controllers, or handlers without external dependencies. Integration tests check how multiple components work together—the API, database, and external services. Contract tests ensure that both the API provider and its consumers adhere to the same contract, whether defined through OpenAPI or Pact. A solid testing strategy covers the API at different levels, runs automatically, and integrates into your CI/CD pipeline. The goal is to catch bugs early, prevent regressions, and reliably document and protect the contract between your API and its clients.
Key Components
Unit Tests for APIs
Unit tests verify small, isolated pieces of your API: handlers, controllers, validation logic, or data mappers. External dependencies like databases or HTTP clients are replaced with mocks or stubs. Unit tests run fast and give quick feedback when you make changes.
Integration Tests
Integration tests verify how your API works with real or containerized dependencies. This includes database access, message queues, external APIs, and authentication services. Testcontainers make it straightforward to spin up databases or other services in your test environment.
End-to-End Tests
End-to-End tests simulate the API from a client’s perspective. They send real HTTP requests and verify the complete response. Tools like Postman, REST Assured, or Supertest are commonly used. E2E tests are slower but cover the entire stack.
Contract Tests
Contract tests verify that both the API and its clients honor the agreed contract. The contract may be defined through OpenAPI, JSON Schema, or frameworks like Pact. Consumer Driven Contracts allow clients to communicate their expectations to the provider.
Consumer Driven Contracts
With Consumer Driven Contracts, clients write their expectations as a formal contract. The provider must satisfy this contract. Pact is a popular framework for this approach. It prevents API changes from silently breaking client code.
The Test Pyramid
The test pyramid dictates that you should have many fast unit tests, fewer integration tests, and only a few slow E2E tests. Contract tests fit into the pyramid between unit and integration levels. They help you verify interface contracts efficiently.
Test Data and Fixtures
Test data should be reproducible and isolated. Fixtures, factories, or setup methods generate the data you need before each test. Database tests should use transactions or fresh databases to keep state isolated between tests.
Mocking and Stubbing
Mocking replaces external dependencies with controlled objects. Stubbing provides predefined responses. Both techniques are essential for unit tests but not for integration tests, where you use real dependencies.
Fuzzing and Negative Tests
Negative tests verify how your API handles invalid input, missing fields, or unexpected data types. Fuzzing automatically generates many random or semi-random inputs to uncover weaknesses.
Performance Tests
Performance tests check response times, throughput, and behavior under load. Tools like k6, JMeter, or Gatling are standard choices. They complement functional tests and matter for APIs with high availability or scalability demands.
CI/CD Integration
API tests should run automatically in your CI/CD pipeline. Unit tests execute on every build, integration tests before merge, contract tests when the API changes, and E2E tests before deployment. Failed tests halt the build early.
Practical Example
A Node.js team tests an order API at all levels.
Unit test for order validation:
describe('Order validation', () => {
test('rejects negative quantity', () => {
const result = validateOrder({ customerId: 1, items: [{ productId: 5, quantity: -1 }] });
expect(result.valid).toBe(false);
expect(result.errors).toContain('quantity must be positive');
});
});
Integration test with testcontainers:
describe('POST /orders', () => {
test('creates an order and persists it', async () => {
const response = await request(app)
.post('/orders')
.send({ customerId: 1, items: [{ productId: 5, quantity: 2 }] })
.expect(201);
expect(response.body.id).toBeDefined();
expect(response.body.status).toBe('created');
const order = await db.query('SELECT * FROM orders WHERE id = ?', [response.body.id]);
expect(order).toHaveLength(1);
});
});
Contract test with Pact:
const pact = new Pact({
consumer: 'web-shop',
provider: 'order-service',
});
await pact.addInteraction({
state: 'order can be created',
uponReceiving: 'a request to create an order',
withRequest: {
method: 'POST',
path: '/orders',
body: { customerId: 1, items: [{ productId: 5, quantity: 2 }] }
},
willRespondWith: {
status: 201,
body: { id: 1, status: 'created' }
}
});
By combining all three levels, the API is tested thoroughly and early in the development cycle.
FAQ: API Testing
1. What is API Testing?
2. What is a unit test for APIs?
3. What is an integration test?
4. What is a contract test?
5. What is Consumer Driven Contract Testing?
6. What is Pact?
7. What is the test pyramid?
8. What is mocking?
9. What are testcontainers?
10. What is an end-to-end test?
11. What are negative tests?
12. What is fuzzing?
13. What are performance tests for APIs?
14. Why should API tests run in CI/CD?
15. What are best practices for API testing?
Next in the API Learning Path
The next article in the API Learning Path covers API Rate Limiting Implementation — how to implement rate limiting in APIs using Token Bucket, Sliding Window, and Redis.
References
- https://martinfowler.com/articles/consumerDrivenContracts.html
- https://docs.pact.io/
- https://www.testcontainers.org/
Recommended Books on Testing and API Design
If you’d like to explore API testing, test automation, and software quality further, we recommend the following books:
API Development
Books about API design, REST, GraphQL, OpenAPI and API architecture
Designing Data-Intensive Applications von Martin Kleppmann
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.
API Design Patterns von JJ Geewax
Bei Amazon ansehenAffiliate-Link: Bei einem Kauf erhalten wir möglicherweise eine Provision.




