API Testing: Unit, Integration, and Contract Tests
Reliable APIs require a thoughtful testing strategy that combines unit tests, integration tests, and contract tests to catch errors early and protect interface contracts.
Overview
API testing encompasses all activities that verify the correctness, security, performance, and stability of an API. Unit tests examine isolated functions, controllers, or handlers without external dependencies. Integration tests verify how multiple components work together—such as the API, database, and external services. Contract tests ensure that both the API provider and consumers honor the same contract, whether defined through OpenAPI, JSON Schema, or Pact. A solid testing strategy covers the API at multiple layers, 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 quickly and provide fast feedback when you make changes.
Integration Tests
Integration tests verify how your API collaborates with real or containerized dependencies. This includes database access, message queues, external APIs, and authentication services. Test containers let you spin up databases and other services in your test environment without manual setup.
End-to-End Tests
End-to-End tests simulate your API from the 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 your entire stack.
Contract Tests
Contract tests verify that the API provider and consumer both uphold their agreed contract. The contract may be defined through OpenAPI, JSON Schema, or frameworks like Pact. Consumer-driven contracts allow consumers to communicate their expectations to the provider.
Consumer-Driven Contracts
In consumer-driven contract testing, clients define their expectations as a contract. The provider must fulfill this contract. Pact is a popular framework for this approach. It prevents API changes from silently breaking client applications.
The Test Pyramid
The test pyramid says you should have many fast unit tests, fewer integration tests, and only a few slow E2E tests. Contract tests fill the gap between unit and integration levels. They help you efficiently verify interface contracts.
Test Data and Fixtures
Test data should be reproducible and isolated. Fixtures, factories, or setup methods generate the data you need before each test. For database tests, use transactions or fresh databases to isolate state between tests.
Mocking and Stubbing
Mocking replaces external dependencies with controlled objects. Stubbing provides predefined responses. Both techniques are important for unit tests, but not for integration tests, where you use real dependencies.
Fuzzing and Negative Tests
Negative tests check how your API handles invalid inputs, missing fields, or unexpected data types. Fuzzing automatically generates many random or semi-random inputs to find weaknesses.
Performance Tests
Performance tests check response times, throughput, and behavior under load. Tools like k6, JMeter, or Gatling are commonly used. They complement functional tests and are critical for APIs with high availability and scalability requirements.
CI/CD Integration
API tests should run automatically in your CI/CD pipeline. Unit tests run on every build, integration tests before merge, contract tests when the API changes, and E2E tests before deployment. Failures stop the build early.
Practical Example
A Node.js team tests an order API at multiple 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 test containers:
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 team tests the API thoroughly and catches issues early.
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 Test Containers?
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 — implementing 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 Reading on Testing and API Design
To deepen your knowledge of API testing, test automation, and software quality, consider these 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.




