Should I test that?

Is contract testing worth it?

Verdict

Yes

Yes, contract testing is worth it between services that different teams deploy on their own schedules: write a Pact consumer test for each request the consumer makes, listing only the fields it reads, and let the provider verify those contracts in CI before each deploy.

Why

Yes, write contract tests between services that different teams deploy on their own schedules. The typical case is an app that calls the HTTP API of a service another team owns, with no shared test before either side deploys. Blast radius is users and Change frequency is regularly, because the provider's API gains or renames fields about once a month. Detectability is eventually, because the provider's tests stay green and a renamed field reaches the consumer as null, shown as an empty value. Reversibility is with-effort, because records the consumer saved without the field need a repair script. Test cost is moderate after a one-time Pact Broker setup: each consumer test takes about an hour, so rule R11 gives Test.

When the decision changes
WhenDecisionWhy
The provider returns prices that the consumer charges to customers' cardsTest mandatory: a contract for each amount and currency field the consumer reads, checked with can-i-deploy before every deployBlast radius rises to money and Reversibility to costly, because wrong charges end in refunds
The provider returns the roles that the consumer checks before it shows another user's recordsTest mandatory: a contract for an allowed and a denied user, verified before every provider deployBlast radius rises to safety-or-legal and Detectability to never, because a changed role value can open records to the wrong users without an error
One team owns both services, deploys them together, and generates the types of both sides from one schemaDo not write contract tests; let the compiler reject a renamed field before mergeDetectability moves to immediately and Reversibility to trivial, because the build fails before anything ships
The API is public, and you do not know who calls itTest minimally: one provider test per endpoint that asserts the fields of the published OpenAPI document, and breaking changes ship as a new API versionChange frequency falls to rarely, because published fields change only with a new API version, and Test cost stays moderate
The provider's API changes a few times a yearTest minimally: one contract for the main request the consumer makes, with the fields it readsChange frequency falls to rarely, so fewer provider releases can break the consumer

What breaks if you don't test

The provider team removes a field in a release that passes all of its own tests, because none of those tests knows which fields other services read. The consumer shows blanks or defaults, and its team learns of the change from a support ticket days later. With several consumers, nobody can say which one a change breaks, so the provider team stops removing old fields.

What you lose if you over-test

A contract that asserts exact values instead of types fails whenever the provider changes its test data. A contract that checks business rules repeats the provider's functional tests, which the Pact documentation places outside contract testing. Each given state needs a handler in the provider's test code, which the provider team maintains for another team's tests.

How to test

  1. For each request the consumer makes, write a consumer test against the Pact mock server, with type matchers and only the fields the consumer reads. Add the error responses it handles, such as a 404.
  2. Publish the contract to a Pact Broker from the consumer's CI.
  3. In the provider's CI, run the verifier with a stateHandlers entry for each given state.
  4. Before either side deploys, run can-i-deploy and stop the deploy when it fails.

When the answer changes

  • One team owns both sides and ships them together.
  • The API is public, so the consumers are unknown.
  • The responses carry amounts the consumer charges, or roles it uses to grant access.

Real incident + Code example

The delivery time that went blank

On a logistics product I worked on, the shipments team renamed eta to estimatedDelivery after a search of their own repository found no other use. Their tests passed. The tracking page read shipment.eta, got undefined, and showed every parcel as "not scheduled yet". Support tickets rose for nine days before someone traced them to the rename. Afterwards the tracking team published a contract like this one:

import { Pact, Matchers } from "@pact-foundation/pact";
import { expect, it } from "vitest";
import { getShipment } from "./shipmentsClient";

const { like } = Matchers;
const provider = new Pact({ consumer: "tracking-web", provider: "shipments-api" });

it("gets the fields the tracking page reads", async () => {
  await provider
    .addInteraction()
    .given("shipment 42 is out for delivery")
    .uponReceiving("a request for shipment 42")
    .withRequest("GET", "/shipments/42")
    .willRespondWith(200, (builder) => {
      builder.jsonBody(like({ status: "out_for_delivery", eta: "2026-03-12T14:00:00Z" }));
    })
    .executeTest(async (mockserver) => {
      const shipment = await getShipment(mockserver.url, "42");
      expect(shipment.eta).toBe("2026-03-12T14:00:00Z");
    });
});

With this contract in the broker, the rename fails the shipments build, because the verifier finds no eta in the response. Fields the tracking page does not read stay out of the contract and remain free to change.

FAQ

What should be the scope of a Pact provider test?

A Pact provider test checks only that the provider answers the requests in the consumers' contracts with the fields they list. Business rules, validation and side effects belong in the provider's own functional tests, according to the Pact documentation.

Can contract tests replace end-to-end tests?

Contract tests can replace end-to-end tests whose only job is to check that two services agree on requests and response fields. Keep a few end-to-end tests for the main user flows, because a contract test does not check what the provider does with a request.

Should I use Pact for a public API?

No, Pact does not fit a public API, because unknown consumers cannot publish contracts. Write one provider test per endpoint that asserts the fields of the published OpenAPI document, and ship breaking changes as a new API version.

Do I need contract tests if one team owns both services?

No, a team that deploys both services together does not need contract tests if it generates the types of both sides from one schema, because the compiler then rejects a renamed field. Add contract tests when the services start to deploy separately.