Skip to content

Lesson 03 · Contract Testing

Beyond the 1Z0-830 exam

When services talk to each other, the expensive bugs are interface mismatches — the provider renamed a field, the consumer still expects the old one. Contract testing catches these without deploying every service together, by verifying both sides against a shared, machine-checkable contract. Pact is the dominant tool.

Objectives

After this lesson you will be able to:

  • Explain consumer-driven contracts and what problem they solve.
  • Describe the consumer and provider verification steps.
  • Say why extra provider fields are allowed but missing ones aren't.
  • Place contract tests in the test pyramid vs full E2E.

The problem: integration without integrating

Full end-to-end tests catch interface mismatches, but they're slow, flaky, and need every service running together. Contract testing gets most of that safety far cheaper: each side is tested independently against a shared contract.

mermaid
flowchart LR
    C[Consumer test] -->|generates| P[Contract / Pact file]
    P -->|verified against| V[Provider test]

Consumer-driven contracts (Pact)

The flow has two halves:

  1. Consumer side — the consumer writes a test against a mock provider, declaring the requests it makes and the response fields it actually uses. Running it produces a contract (a Pact file).
  2. Provider side — the provider replays the contract against the real provider and verifies every interaction still holds.

If the provider later drops or renames a field a consumer relies on, the provider's verification fails — before deployment, not in production.

Required vs extra fields

The crucial rule, which the lab encodes: a consumer specifies only the fields it uses.

java
ContractValidator contract = new ContractValidator()
    .expect("id", Long.class)
    .expect("name", String.class);

// Provider adds a new field later — still satisfies the contract (forward-compatible):
contract.verify(Map.of("id", 1L, "name", "Gizmo", "addedLater", true)).satisfied(); // true

// Provider drops a required field — contract broken:
contract.verify(Map.of("id", 1L)).satisfied();                                       // false → "missing field: name"
  • Extra provider fields → fine. Providers can evolve without breaking consumers.
  • Missing or wrong-typed required fields → break the contract.

This asymmetry is what makes independent evolution safe.

Gotcha — contract tests aren't end-to-end tests

A contract test verifies the shape and semantics of the interface, not that the whole system produces the right business outcome. You still want a thin layer of real E2E tests. Contract testing replaces the bulk of cross-service integration tests, not all of them.

Where it sits in the pyramid

In the test pyramid (Module 15), contract tests live just below E2E: more confidence than a unit test about cross-service compatibility, far cheaper and more stable than full integration. For a microservice architecture they're often the highest-leverage tests you can add.

SDET note

The lab's ContractValidator is Pact's core idea in ~30 lines: declare the consumer's expectations, verify a provider response, and report precise violations (missing field: name, wrong type for id). Real Pact adds a broker to share contracts between teams and CI, plus matching rules — but the principle is exactly this.

Key Takeaways

  • Contract testing verifies that services agree on their interface, testing each side independently against a shared contract.
  • Consumer-driven: the consumer declares the fields it uses → a contract; the provider verifies it still satisfies them.
  • Extra provider fields are fine; missing/wrong-typed required fields break the contract — enabling safe independent evolution.
  • Pact is the standard tool (with a broker to share contracts); contract tests sit just below E2E in the pyramid and don't replace it.

Lesson Quiz

Lesson Quiz · Contract Testing0 / 5
  1. Contract testing primarily catches…

    • AUI rendering bugs
    • BInterface mismatches between services (renamed/removed fields), without full E2E deployment
    • CSlow database queries
    • DMemory leaks
  2. In consumer-driven contracts, who declares the expected response shape?

    • AThe provider
    • BThe consumer — it specifies the fields it actually uses
    • CThe database
    • DThe CI server
  3. A provider adds a new field to its response. The existing contract…

    • ABreaks
    • BStill holds — extra fields are forward-compatible; only missing/wrong-typed required fields break it
    • CMust be deleted
    • DRequires a new consumer
  4. What fails when a provider drops a field a consumer relies on?

    • ANothing until production
    • BThe provider's contract verification fails before deployment
    • CThe consumer's unit tests only
    • DThe UI tests
  5. Contract tests vs end-to-end tests:

    • AContract tests fully replace E2E
    • BContract tests verify the interface shape cheaply; you still want a thin layer of real E2E
    • CE2E tests are unnecessary
    • DThey test the same thing

Next: Module 20 · CI/CD & Test Infrastructure. This module's lab is in labs/src/main/java/com/jse21/m19_specialized/.