Contract Testing with Pact — Complete Guide
In this tutorial, you will learn about Contract Testing with Pact. We cover key concepts, practical examples, and best practices to help you master this topic.
Contract testing verifies that API providers and consumers agree on the interface. Pact implements consumer-driven contract testing where consumers define expected interactions and providers verify they can fulfill them.
What You'll Learn
- Consumer-driven contract testing concepts
- Writing Pact tests for consumers
- Provider verification against pacts
- Pact flow and pact file lifecycle
- CI/CD integration with Pact Broker
Why It Matters
Contract testing catches breaking changes before deployment. It ensures that API changes do not break consumers. It is faster than end-to-end tests and more reliable than unit tests for integration points.
Real-World Use
Spotify uses contract testing for microservice compatibility. Atlassian uses Pact for internal API contracts. Many financial services use Pact to verify API agreements between teams.
flowchart LR
Consumer[Consumer Team] --> Test[Consumer Test]
Test --> Pact[Pact File Generated]
Pact --> Broker[Pact Broker]
Broker --> Provider[Provider Team]
Provider --> Verify[Provider Verification]
Verify --> Result[Verification Result]
Result --> Broker
Broker --> Consumer
Teacher Mindset
Contract tests are not full integration tests. They verify that the consumer's expectations match the provider's actual behavior. Consumers write tests first. Providers verify against those tests.
Code Examples
// Example 1: Consumer Pact test (Node.js)
const { PactV3, MatchersV3 } = require('@pact-foundation/pact');
const { eachLike, like } = MatchersV3;
const provider = new PactV3({
consumer: 'UserServiceClient',
provider: 'UserService',
port: 1234,
});
describe('User API contract', () => {
it('returns a user by ID', async () => {
await provider.addInteraction({
states: [{ description: 'user with ID 1 exists' }],
uponReceiving: 'a request for user 1',
withRequest: {
method: 'GET',
path: '/api/users/1',
headers: { Accept: 'application/json' },
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: like(1),
name: like('Alice'),
email: like('alice@test.com'),
},
},
});
await provider.executeTest(async (mockServer) => {
const client = new UserClient(mockServer.url);
const user = await client.getUser(1);
expect(user.id).toBe(1);
expect(user.name).toBe('Alice');
});
});
});
# Example 2: Provider verification (Ruby)
require 'pact/provider/rspec'
Pact.service_provider 'UserService' do
honours_pact_with 'UserServiceClient' do
pact_uri 'http://pact-broker/pacts/provider/UserService/consumer/UserServiceClient/latest'
end
end
Pact.provider_states_for 'UserServiceClient' do
provider_state 'user with ID 1 exists' do
set_up do
User.create(id: 1, name: 'Alice', email: 'alice@test.com')
end
end
end
# Example 3: Pact Broker CI integration
# docker-compose.yml for Pact Broker
version: '3'
services:
pact-broker:
image: pactfoundation/pact-broker
ports:
- "9292:9292"
environment:
PACT_BROKER_DATABASE_URL: postgres://postgres:password@postgres/pact
postgres:
image: postgres
environment:
POSTGRES_PASSWORD: password
Common Mistakes
- Writing contract tests that cover too many scenarios (keep focused on consumer needs)
- Not running provider verification in CI
- Allowing contract tests to become full integration tests with database setup
- Ignoring pact verification failures in deployment pipelines
- Not versioning pact files or tagging them for different environments
Practice
- Write a consumer Pact test for a GET endpoint.
- Write the provider verification for the same interaction.
- Publish the pact file to a Pact Broker.
- Run provider verification in a CI pipeline.
- Challenge: Set up a Pact workflow with multiple consumers and a shared provider.
FAQ
Mini Project
Set up consumer-driven contract testing for a microservice pair. Create a consumer Pact test for 3 endpoints. Set up provider verification with states. Publish pacts to a broker and run verification in CI.
What's Next
Next, you will learn about mock servers for API testing with WireMock and Pretender.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro