intermediate·10 min read·Updated Oct 24, 2026

What Is Pact Contract Testing? Consumer-Driven Contracts, Fairly Compared

Pact turns each consumer's expectations into a contract the provider must verify. See how it works on one interaction, where it beats spec-driven contracts, where it doesn't, and where Shift-Left API fits.

This is lesson 5.7 of OpenAPI, Contract and Schema Testing. Lesson 5.6 named two places a contract can live; this lesson is about the second, consumer-driven contracts, and it is mostly concept. No product feature is needed for the first two thirds.

What is Pact contract testing?

Pact contract testing is consumer-driven: each client of an API writes down the requests it makes and the parts of the response it reads, and the API's team verifies that the real API still satisfies every client's list. Pact is the open-source tool that does this. A consumer's expectations become a pact file; the provider replays the requests in it against the real API; and a deployment is allowed only when every verified contract still passes.

The idea behind it: an API only needs to keep the promises someone relies on. A field no client reads can be renamed freely. A field one client reads can't.

How Pact works, in four steps

  1. The consumer writes a test. In its own codebase and language, the client team describes an interaction: a request, and the response it expects. The test runs against a Pact mock provider, so it is fast and needs no real API.
  2. Pact writes a contract. A passing consumer test produces a pact file: a JSON record of each interaction.
  3. The contract is shared. Pact files are published to a Pact Broker (open source) or PactFlow (hosted), versioned by consumer and provider.
  4. The provider verifies it. The provider's build fetches every consumer's pact, replays each request against the real API, and checks the responses. Before a deploy, the can-i-deploy check asks the broker whether this version has passed against everything it will meet in production.

Provider states connect the two sides: an interaction says "given order ffffffff-... exists", and the provider sets that data up before replaying the request.

A Pact contract testing example

Picture an order page that calls the sandbox store once:

GET/api/v1/orders/ffffffff-ffff-ffff-ffff-ffffffffffff?expand=product
The one request the order page in this lesson makes.
curl -X GET 'https://demo.totalshiftleft.ai/api/v1/orders/ffffffff-ffff-ffff-ffff-ffffffffffff?expand=product'

The response carries more than a dozen fields. The order page reads two: data.status and data.product.name. Its pact describes only those, matched by type rather than exact value. Simplified, in the style of a pact file:

{
  "consumer": { "name": "order-page" },
  "provider": { "name": "store-api" },
  "interactions": [{
    "description": "a request for a pending order with its product",
    "providerState": "order ffffffff-ffff-ffff-ffff-ffffffffffff exists",
    "request": {
      "method": "GET",
      "path": "/api/v1/orders/ffffffff-ffff-ffff-ffff-ffffffffffff",
      "query": "expand=product"
    },
    "response": {
      "status": 200,
      "body": { "data": { "status": "pending", "product": { "name": "Standing Desk" } } },
      "matchingRules": {
        "$.body.data.status": { "match": "type" },
        "$.body.data.product.name": { "match": "type" }
      }
    }
  }]
}

Now the provider team removes notes from orders. The pact still passes: the order page never read it. Then they rename product.name to product.title. Verification fails, the broker records it, and can-i-deploy stops the release before the order page breaks.

Consumer-driven vs spec-driven, fairly

The spec-driven alternative, covered in lesson 5.6, makes the provider's OpenAPI spec the contract. Neither approach is better everywhere:

Consumer-driven (Pact)Spec-driven (OpenAPI)
Contract comes fromEach consumer's testsThe provider's spec
Knows which fields clients useYes, preciselyNo; it must assume any documented field is in use
Works for unknown clients (public APIs)No: a consumer must write a pactYes
CoversThe interactions consumers wroteEvery documented operation and rule
Who does the workEvery consumer team, in codeThe provider team, mostly
Extra infrastructureA broker, and verification in every provider buildA spec diff in CI
Catches consumer misusePartly, in the consumer's own testsNo

Pact's strength is precision: it lets providers change anything nobody depends on, and it proves a deploy is safe for the specific clients in production. Its cost is coordination: every client team must adopt it, write and maintain tests, and keep provider states working. Between internal services owned by teams who talk to each other, that trade is often worth it. For a public API with unknown clients, it can't work alone, because there is no one to write the pacts.

Contract testing tools like Pact

  • Pact, with libraries for JavaScript, the JVM, .NET, Go, Python, Ruby, Rust and more, and the Pact Broker or PactFlow to share contracts.
  • Spring Cloud Contract, on the JVM: contracts written in a DSL, from which it generates provider tests and stubs for consumers.
  • Spec-driven tools, which treat OpenAPI as the contract: Specmatic generates contract tests and stubs from a spec; Schemathesis generates property-based tests from it.
  • Bi-directional contract testing, in PactFlow, which compares consumer pacts with the provider's OpenAPI spec instead of replaying requests.

For a hands-on Pact tutorial with CI, read Pact contract testing: a hands-on tutorial with CI.

Pact contract testing alternatives: choosing

Ask three questions:

  1. Do you know all your consumers? If not, you need a spec-driven contract for at least the public surface.
  2. Will consumer teams write and maintain contract tests? If not, a consumer-driven program stalls after the first service.
  3. Is your spec accurate? If not, spec-driven contracts test the wrong thing until it is fixed.

Many organisations use both: spec-driven contracts for the published API, Pact between internal services, and bi-directional checks where they meet.

Where Shift-Left API fits

Shift-Left API is a spec-driven tool, on the provider side. It does not read or write pact files, and it has no broker or can-i-deploy step, so it doesn't replace Pact for consumer-driven contracts. What it does on the spec-driven side:

  • Generates provider tests from the OpenAPI spec (lesson 5.3).
  • Classifies each re-imported spec change as breaking or non-breaking before it is applied (lesson 5.5).
  • Compares the responses of passing tests with the documented response schema.
  • Spins up mock servers from the spec, so consumers can build against the documented answers (lesson 9.9). The mocks follow the spec's examples or schema placeholders; they don't record or replay consumer expectations.

You can still write a consumer's expectations as a test against the provider, as in the lab below. That checks the provider today; it is not a pact, because no consumer build produces it. The API contract testing page describes the spec-driven workflow.

Try it yourself

Write the order page's contract as one Shift-Left API test: Get a single order by ID on ffffffff-ffff-ffff-ffff-ffffffffffff, with expand set to product. Add two Quick JSON assertions: data.status is one of pending, processing, completed, cancelled, and data.product.name is of type string. Run it. Then ask: which fields of the response is the order page free to lose?

Recap

  • What is Pact contract testing? Consumers record the interactions they rely on, and providers verify every consumer's pact before they deploy.
  • A Pact contract testing example covers only the fields a consumer reads, so providers can change everything else safely.
  • Contract testing tools like Pact suit internal services with known consumers; spec-driven contracts are the main Pact contract testing alternative for public APIs, and many teams use both.

Next: lesson 5.8, an API versioning example, for when a contract has to break.

Hands-on lab

Try it: Write the order page's contract as one Shift-Left API test: Get a single order by ID on the pending order ffffffff-ffff-ffff-ffff-ffffffffffff with expand set to product, with Quick JSON assertions that data.status is one of pending, processing, completed, cancelled and that data.product.name is of type string.

Enter your email to open the lab steps. One unlock opens every lab in the learning center.

No spam. Protected by reCAPTCHA — Google Privacy & Terms.

Frequently asked questions

What is a Pact contract testing example?+

An order page calls GET /api/v1/orders/{id}?expand=product and reads two fields: the order's status and the product's name. Its Pact test describes that request and those two fields, runs against a Pact mock of the provider, and writes a pact file. The provider team then replays the request against the real API and checks the two fields are still there with the same types.

What are some contract testing tools like Pact?+

Pact is the best-known consumer-driven tool, with a Pact Broker or the hosted PactFlow to share contracts. Spring Cloud Contract, on the JVM, generates provider tests and stubs from contracts written in a DSL. Spec-driven tools treat the OpenAPI document as the contract instead, generating conformance tests and mocks from it; Specmatic and Schemathesis are examples, and so is Shift-Left API.

What are the alternatives to Pact contract testing?+

The main alternative is spec-driven contract testing: the provider's OpenAPI spec is the contract, every change to it is diffed for breaking changes, and the running API is tested against it. It suits public APIs and clients you don't control. Bi-directional contract testing, offered by PactFlow, combines the two by comparing consumer pacts with the provider's spec.

Related lessons

Read more on the blog