Validate API contracts using consumer-driven contract testing (Pact, Spring Cloud Contract). Use when performing specialized testing. Trigger with phrases like "validate API contract", "run contract tests", or "check consumer contracts".
Validate API contracts between services using consumer-driven contract testing to prevent breaking changes in microservice architectures. Supports Pact (the industry standard for CDC testing), Spring Cloud Contract (JVM), and OpenAPI-diff for specification comparison.
like(), eachLike(), term()) instead of exact values.pact-broker publish with the consumer version and branch/tag.can-i-deploy to check compatibility before releasing either side.openapi-diff to detect breaking changes.pacts/ directorycan-i-deploy deployment gates| Error | Cause | Solution |
|---|---|---|
| Provider verification fails | Provider response does not match consumer expectations | Check if the contract is outdated; update consumer tests if the change is intentional; fix provider if regression |
can-i-deploy blocks release | Consumer has unverified or failed contracts | Run provider verification; check if the right version tags are published; verify Pact Broker webhook fired |
| Pact Broker connection error | Broker URL or credentials misconfigured | Verify PACT_BROKER_BASE_URL and PACT_BROKER_TOKEN environment variables; check network connectivity |
| Provider state not found | Consumer test references a state the provider does not implement | Add the missing provider state setup function; align state names between consumer and provider |
| Too many contracts to maintain | Every consumer-provider pair has extensive contracts | Focus on critical interactions; use matchers instead of exact values; consolidate similar interactions |
Pact consumer test (JavaScript):
import { PactV4 } from '@pact-foundation/pact';
const provider = new PactV4({ consumer: 'Frontend', provider: 'UserAPI' });
describe('User API Contract', () => {
it('fetches a user by ID', async () => {
await provider
.addInteraction()
.given('user with ID 1 exists')
.uponReceiving('a request for user 1')
.withRequest('GET', '/api/users/1', (builder) => {
builder.headers({ Accept: 'application/json' });
})
.willRespondWith(200, (builder) => { # HTTP 200 OK
builder
.headers({ 'Content-Type': 'application/json' })
.jsonBody({
id: like('1'),
name: like('Alice'),
email: like('[email protected]'),
});
})
.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/api/users/1`);
const user = await response.json();
expect(user.name).toBeDefined();
});
});
});
Provider verification test:
import { Verifier } from '@pact-foundation/pact';
describe('User API Provider Verification', () => {
it('validates consumer contracts', async () => {
await new Verifier({
providerBaseUrl: 'http://localhost:3000', # 3000: 3 seconds in ms
pactBrokerUrl: process.env.PACT_BROKER_BASE_URL,
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
provider: 'UserAPI',
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
stateHandlers: {
'user with ID 1 exists': async () => {
await db.users.create({ id: '1', name: 'Alice', email: '[email protected]' });
},
},
}).verifyProvider();
});
});
can-i-deploy CI check:
pact-broker can-i-deploy \
--pacticipant Frontend \
--version $(git rev-parse HEAD) \
--to-environment production \
--broker-base-url $PACT_BROKER_URL \
--broker-token $PACT_BROKER_TOKEN