Contract Testing: API Reliability Across Services

Contract testing verifies that the producer of an API and its consumers agree on the shape of data and behavior. It is the glue that keeps microservices from breaking each other. Unlike end-to-end tes

May 10, 2026 · 3 min read · Testing Guides

Contract testing verifies that the producer of an API and its consumers agree on the shape of data and behavior. It is the glue that keeps microservices from breaking each other. Unlike end-to-end tests, contract tests are fast, reliable, and run without a full environment. This guide covers consumer-driven contracts specifically.

The problem contract tests solve

Service A calls Service B. A is a client; B is a server. Both evolve. A breaking change in B (removed field, renamed endpoint, changed semantics) breaks A.

Options:

  1. Manual coordination. B team emails A team. Slow, error-prone.
  2. E2E tests. A and B deployed together, test exercises full flow. Slow, expensive.
  3. Contract tests. A declares expectations; B verifies. Runs in isolation.

Contract tests catch the A-B breakage at B's PR time, not at runtime.

Consumer-driven contracts (CDC)

The consumer drives what the contract must support. Why? Consumers know what they need. Producers might add fields; consumers only need specific ones.

Flow:

  1. Consumer declares expected requests and responses (the contract)
  2. Contract published to shared registry
  3. Producer's CI verifies it satisfies the contract
  4. Any producer change that breaks the contract → red build

Tools

Pact

The canonical. Libraries for most languages. Broker for contract registry. Mature.

Spring Cloud Contract

Java / Spring focused. Similar concept.

WireMock

HTTP mocking with record/playback. Not strictly CDC but adjacent.

Mountebank

Multi-protocol stubbing.

Hand-rolled

OpenAPI spec + schema validation at runtime. Simple but limited.

Pact example

Consumer side


@Pact(consumer = "ClientA", provider = "ServiceB")
public RequestResponsePact pact(PactDslWithProvider builder) {
    return builder
        .given("user with id 42 exists")
        .uponReceiving("get user 42")
        .path("/users/42")
        .method("GET")
        .willRespondWith()
        .status(200)
        .body(newJsonBody(o -> o
            .stringType("id", "42")
            .stringType("name", "Alice")
        ).build())
        .toPact();
}

During consumer testing, Pact spins up a stub server matching this contract. Tests run against it. Contract is published to broker on pass.

Producer side


@State("user with id 42 exists")
public void userExists() {
    // Set up state in producer's DB
    userRepository.save(new User("42", "Alice"));
}

During producer CI, Pact fetches contracts from broker. For each contract, sets up state, replays requests, verifies responses match. Fails build on mismatch.

What contracts cover

What they do not cover:

Integration with CI

Anti-patterns

1. Producer-driven contracts

Producer says "here's what I return." Consumer adapts. Gives producer freedom to ship anything, consumers break.

2. Ignoring state

@State setup skipped or incorrect. Contracts pass for wrong reasons.

3. Broker outdated

Contracts in broker are stale. Producer verifies against non-existent consumers.

4. Coupling tests

Consumer's unit tests do double-duty as contract tests. Mixing concerns.

5. No can-i-deploy gate

Contracts exist but deploy still allowed with incompatible versions.

When contracts are not enough

Contract tests + integration + E2E = layered coverage.

How SUSA handles API contracts

SUSA captures all APIs observed during exploration. From this:


susatest-agent test myapp.apk --persona curious --steps 200
# results/api_surface.json has inferred endpoints
# results/evolution.json has deltas from previous session

This is post-hoc contract discovery. Useful for legacy systems without explicit contracts.

Starting with Pact

  1. Pick one consumer-producer pair. High-traffic, business-critical.
  2. Add Pact to consumer tests. Start with one endpoint.
  3. Set up broker (Pact Broker self-hosted or PactFlow SaaS).
  4. Producer CI verifies.
  5. Expand coverage: more endpoints, more consumers.

Contract testing matures teams. Microservices without contracts are fragile; with contracts, durable.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free