← Recipes

阅读中文版本

Verify Provider Contracts with Consumer-Driven Pact

Publish consumer expectations as pacts, verify the provider against them in CI, and break the build on any contract drift before deploy.

~45 mintestingcontract-testingci

Task

Set up consumer-driven contract tests so a provider change that breaks any consumer fails CI instead of production.

When to use this

The API team renames a field. Their tests pass, three consumers break silently, and the discovery happens in production logs over two days.

Prerequisites

  • A Pact broker or another shared pact exchange
  • One consumer with tests expressing its expectations
  • CI access on the provider side to run verification

Steps

1. Write the consumer test that states its needs.

describe("GET /orders/{id}", () => {
  it("returns the fields the order list uses", () => {
    return provider.mockResponse({
      id: 1,
      totalCents: 4200,
      status: "shipped",
    });
  });
});

Expected output: The consumer test documents exactly the fields it depends on, no more, no less.

2. Publish the pact from consumer CI.

npx @pact-foundation/cli pact-broker publish pacts/ --consumer-app-version $GIT_SHA --tag main

Expected output: The pact is visible in the broker, tagged to the consumer version that produced it.

3. Verify the provider against every published pact.

const { Verifier } = require("@pact-foundation/pact");

new Verifier({
  providerBaseUrl: "http://localhost:3000",
  pactBrokerUrl: process.env.PACT_BROKER_URL,
  publishVerificationResult: true,
  providerVersion: process.env.GIT_SHA,
}).verifyProvider();

Expected output: The provider CI replays every consumer expectation against the real API and reports results to the broker.

4. Fail the provider build on broken contracts.

const success = await verifier.verifyProvider();
if (!success) {
  console.error("provider broke a consumer contract");
  process.exit(1);
}

Expected output: The field rename now fails provider CI the moment it lands, naming the consumer it would break.

5. Check can-i-deploy before releasing either side.

npx @pact-foundation/cli can-i-deploy --pacticipant orders-api --version $GIT_SHA --to-environment production

Expected output: The broker answers whether this provider version is verified against every consumer currently in production.

Troubleshooting

Problem

Provider verification fails on test data mismatch

Solution

The provider fixture differs from the pact expectation; align state setup with provider states declared in the pact, and keep fixtures minimal.

Problem

Too many pacts slow provider CI

Solution

Filter verification to changed consumers via the broker webhook mode, or run full verification nightly and on-demand for releases.

Problem

Consumers do not write pacts

Solution

Start with the one consumer that broke last; contract testing spreads by demonstrated incident prevention, not mandate.

Limitations

  • Pact verifies request-response shape and status semantics, not performance or business rules.
  • The broker becomes shared infrastructure; someone must own its availability and retention policy.

The consumer defines the contract

Provider-side API tests prove the provider agrees with itself, which is not the contract that matters. Consumer-driven contracts invert the authority: each consumer publishes the exact fields it needs, and the provider must satisfy every published expectation. The rename that passes provider tests then fails verification against the consumer pact, in CI, with the affected team named in the failure.

new Verifier({
  providerBaseUrl: "http://localhost:3000",
  pactBrokerUrl: process.env.PACT_BROKER_URL,
  publishVerificationResult: true,
  providerVersion: process.env.GIT_SHA,
}).verifyProvider();

can-i-deploy is the release gate

Verification records which provider versions satisfy which consumer pacts. The can-i-deploy query turns that history into a deploy decision: this exact version is safe for production only if verified against every consumer already there. It replaces the folklore of we think all clients upgraded with a broker-recorded fact.

Start with the last incident

Adoption stalls when sold as a platform initiative. Start with the integration that broke most recently: one consumer, one pact, one verification job. The first time a provider PR fails with the name of the consumer it would have broken, the next team volunteers itself.