← Workflows

阅读中文版本

Consumer-Driven Contract Testing Adoption Workflow

Adopt contract tests between services incrementally: publish consumer expectations, verify provider against them in CI, and retire brittle integrated tests.

advanced~40 mintest-strategycontract-testingmicroservices

Objective

Replace heavy end-to-end environments with consumer-driven contracts that catch breaking changes in CI at each service independently.

Environment

  • Two or more services in different repos with an integration between them
  • A contract broker or shared contract storage
  • CI pipelines for both consumer and provider

Steps

1. Pick the highest-pain integration

Start with the pair whose breaking changes caused the most incidents, not the easiest one, since the program must prove value on real pain.

Expected state: The pilot pair is chosen by incident history and both teams have agreed.

2. Write the consumer contract first

The consumer service defines the interactions it depends on (request shape, response fields, status semantics) as executable contract tests.

Expected state: A failing consumer test defines the expectation, and the contract publishes to the broker.

3. Verify the provider against it

The provider's CI fetches the published contract and replays it against a running instance, failing the build on any divergence.

Expected state: A provider change breaking a consumer expectation fails provider CI before merge.

4. Retire the equivalent E2E test

Once contracts cover the interaction, remove the corresponding end-to-end test that required both services deployed together.

Expected state: The E2E suite shrinks by the covered interactions, and pipeline time drops measurably.

5. Expand pair by pair with the same loop

Repeat define-publish-verify-retire for the next integration, keeping each adoption small enough to complete in a week.

Expected state: Coverage of integrations by contracts grows incrementally with retirement evidence per pair.

Expected outcome

Breaking changes between services caught in each side's CI independently, with the slow multi-service E2E environment shrinking as contracts replace it.

Verification

A deliberately breaking provider change fails its own CI via the consumer contract; E2E suite runtime has dropped as covered interactions retired.

The contract replaces the shared environment

Verifying integrations by deploying everything together is slow and flaky, and it finds breakage after the merge. Consumer contracts verify the same fact — provider still satisfies consumer — inside each service’s CI, in seconds, before anything merges.

pair: web (consumer) -> orders-api (provider)

consumer: web
  contract: GET /orders/{id} -> 200 {id, total_cents, status}
  test: expects total_cents > 0 for fixture order
  -> publishes contract v3 to broker

provider: orders-api CI
  fetches web@v3 contract
  replays against running instance:
    GET /orders/demo-1 -> 200 {total_cents: -5}  FAIL
  build blocked BEFORE merge

retired: e2e_checkout_full (needed web+orders+db+cache)
  pipeline: 41 min -> 27 min

Start with the worst integration

The temptation is piloting contracts on the simplest service pair, which proves nothing to skeptics. Choosing the pair that caused last quarter’s incidents means the first caught breakage is a demonstrable win, which is what earns the second and third pair.

Contracts must retire tests to pay

Adding contracts while keeping every E2E test just adds maintenance. The define-verify-retire loop is complete only when the multi-service test for that interaction is deleted, and the pipeline minutes saved are the program’s visible budget for the next pair.