Skip to main content
Test Automation Lab
HomeTestingPlaygroundsGuidesToolsRecipesBlog
© 2026 Lucas Câmara de Souza
GitHubLinkedIn

Contract Testing Playground

A contract is the consumer’s expectation of the provider’s response. When the provider drifts — a field disappears, a type flips, a key is renamed — the consumer breaks. Consumer-driven contract testing catches that before it ships.

Work through each mismatch below: compare what the consumer expects against what the provider sends, then align the provider and watch the breaking-change count fall to zero. Not every change is breaking — see how additive fields and new enum values are treated. More patterns live in the recipes.

✗ Contract broken — 4 breaking changes remaining
  • breaking

    Required field is missing

    user.email

    The consumer needs "email" to send notifications, but the provider stopped including it. Required fields that vanish break every consumer that reads them.

    Consumer expects

    email: string (required)

    Provider sends

    { "id": 1, "name": "Ada" }

    Aligned

    { "id": 1, "name": "Ada", "email": "ada@example.com" }
  • breaking

    Field type changed

    user.id

    The consumer parses "id" as a number. The provider now returns it as a string, so arithmetic and strict equality silently break.

    Consumer expects

    id: number (required)

    Provider sends

    { "id": "1" }

    Aligned

    { "id": 1 }
  • breaking

    Field was renamed

    user.fullName

    The provider renamed "fullName" to "name". The consumer still reads "fullName" and now gets undefined — a rename is a remove plus an add.

    Consumer expects

    fullName: string (required)

    Provider sends

    { "name": "Ada Lovelace" }

    Aligned

    { "fullName": "Ada Lovelace" }
  • breaking

    Non-nullable field became null

    user.createdAt

    The consumer expects "createdAt" to always be a date string and calls .slice() on it. The provider now sends null, throwing at runtime.

    Consumer expects

    createdAt: string (required, non-null)

    Provider sends

    { "createdAt": null }

    Aligned

    { "createdAt": "2026-06-25T10:00:00Z" }
  • warning

    New enum value appeared

    user.status

    The consumer only handles "active" | "suspended". The provider added "archived". Tolerant consumers cope, but a strict switch would miss it — handle the default case.

    Consumer expects

    status: 'active' | 'suspended'

    Provider sends

    { "status": "archived" }

    Aligned

    // Consumer adds a default branch, or agrees to extend the enum
  • safe

    Provider added an extra field

    user.lastLogin

    The provider added "lastLogin", which the consumer does not read. Additive changes are backward-compatible — no action required, just acknowledge it.

    Consumer expects

    (not part of the contract)

    Provider sends

    { "lastLogin": "2026-06-24T22:00:00Z" }

    Aligned

    // Safe: the consumer ignores unknown fields