Consumer-driven contract tests

Microservices · lesson 18 of 33 · 4 min read

Catch a breaking API change inside the provider build instead of in production.

Open this lesson in the learning hub

Key points

  • Integration testing every pair of services does not scale. A contract pins only the shape each consumer actually depends on.
  • The consumer states what it needs. The provider build replays that contract against the real controller on every commit.
  • Rename a field a consumer reads and the provider build goes red, so the break never reaches a deployment.
  • Spring Cloud Contract keeps contracts in the provider repo and generates its tests and the consumer stubs. Pact is the polyglot option.
  • Contracts check shape, status and headers, not business rules. They replace cross-service integration tests, not your unit tests.

Example

# contracts/shouldReturnOrder.yml - lives in the PROVIDER repository.
description: an order can be fetched by id
request:
  method: GET
  url: /v1/orders/o-1042
response:
  status: 200
  headers:
    Content-Type: application/json
  body:
    id: o-1042
    status: PLACED
    totalCents: 4999          # rename this field and the provider build fails
  matchers:
    body:
      - path: $.totalCents
        type: by_type

# Provider build: the plugin generates a test that calls the real controller.
# Consumer build: the same file is published as a WireMock stub, used with
#   @AutoConfigureStubRunner(ids = "com.example:orders:+:stubs",
#                            stubsMode = StubsMode.LOCAL)

Let the consumer say what it needs, then make the provider build prove it still holds.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Microservices course, and every lesson in it is listed on the Microservices contents page.