Contract Testing (Pact)
Contract testing is a technique for testing an integration point by checking each application in isolation to ensure the messages it sends or receives conform to a shared understanding that is documented in a "contract".
1. The Problem with Integration Tests
In a microservices architecture, testing how services communicate is challenging.
- Mocking: Unit tests with mocks are fast but can drift from reality (the "it works on my machine" problem).
- End-to-End (E2E): Spinning up all services is slow, brittle, and expensive to maintain.
Contract testing bridges this gap. It verifies that the Consumer (e.g., a Frontend) and the Provider (e.g., a Backend API) can communicate without spinning up the full system.
2. Consumer-Driven Contracts (CDC)
The most common approach is Consumer-Driven Contract testing.
- Consumer: Defines the exact request it sends and the response it expects. This expectation is the "Contract".
- Provider: Verifies that it can fulfill that contract.
3. How Pact Works
Pact is the de-facto standard tool for contract testing.
Step 1: The Consumer Side
The consumer writes a unit test using the Pact SDK. Instead of hitting the real API, it hits a Mock Provider spun up by Pact.
- The consumer defines: "When I send
GET /users/1, I expect a200 OKwith body{ "id": 1, "name": "Alice" }". - If the test passes, Pact generates a Contract File (a JSON file called a "pact").
Step 2: The Pact Broker
The generated pact file is uploaded to a central Pact Broker. This acts as a repository for all contracts between services.
Step 3: The Provider Side
The provider downloads the pact file from the Broker. It then replays the requests defined in the pact against the real running provider service (or a local instance).
- If the provider returns a response that matches the contract, the verification passes.
4. Benefits
- Fast Feedback: Tests run at unit test speed.
- Independence: Teams can deploy independently. If the contract verification passes, you know you haven't broken the consumer.
- Documentation: The contracts serve as up-to-date documentation of how services interact.
5. Example Scenario
Contract: Consumer needs id and name.
Provider Response: { "id": 1, "name": "Alice", "age": 30 }
- Result: ✅ Pass. The provider returns more data than needed, but the required fields (
id,name) are present and correct.
Provider Response: { "id": 1, "fullName": "Alice" }
- Result: ❌ Fail. The consumer expects
name, but the provider sentfullName. This would break the consumer.
programming/software-testing-basics programming/microservices-architecture programming/rest-apis