Loslegen
Javaneer
Zurück zur Stufe
Stufe 5·API- & Vertragsdesign

Verträge & Consumer-Driven Testing

OpenAPI als maschinenlesbarer Vertrag und Consumer-Driven Contract Testing (Pact / Spring Cloud Contract), das eine brechende Änderung erwischt, bevor sie in Produktion gelangt.

15 Min. LesezeitExperte

Deutsche Übersetzung in Arbeit

Diese Lektion ist noch nicht ins Deutsche übersetzt und wird daher auf Englisch angezeigt. Der Rest der Seite ist vollständig lokalisiert.

Auf dieser Seite

"An API is a contract" has been this module's refrain. This lesson makes the contract literal and executable: a machine-readable OpenAPI specification that documents the API precisely, and consumer-driven contract testing that fails your build the moment you make a change that would break a real consumer. Together they turn "don't break the API" from a hope into an automated guarantee.

OpenAPI: the machine-readable contract

OpenAPI (formerly Swagger) is a standard format - YAML or JSON - describing every endpoint, its parameters, request/response schemas, and status codes:

paths:
  /accounts/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Account' }
        '404':
          content:
            application/problem+json:
              schema: { $ref: '#/components/schemas/Problem' }

Because it's machine-readable, one spec drives many things: interactive docs (Swagger UI), client SDK generation in any language, server stubs, and request/response validation. Two workflows exist:

  • Code-first - annotate your controllers (springdoc-openapi) and generate the spec from the code. Easy, but the spec follows the code.
  • Design-first - write the OpenAPI spec first, agree it with consumers, then implement against it. The spec becomes the source of truth and the contract is negotiated before a line of code.

Design-first is powerful for APIs with external consumers: the contract is agreed up front, teams work in parallel against the spec, and drift is caught by validating the implementation against it.

The limit of OpenAPI alone

OpenAPI documents the contract, but it doesn't prove your running service still honors it, nor that a change won't break a specific consumer. A spec can say "returns an Account" while the code quietly drops a field a consumer depended on. You need tests that verify the real interaction - and specifically, tests driven by what consumers actually use.

Consumer-driven contract testing

The key insight: a provider serves many consumers, and each consumer uses only part of the API. A consumer- driven contract (CDC) captures each consumer's actual expectations and verifies the provider against them:

1. The CONSUMER writes a test describing exactly what it needs:
   "When I GET /accounts/42, I expect a 200 with fields `id` and `balance`."
   This generates a CONTRACT (a pact file).

2. The PROVIDER runs that contract against its real implementation:
   the provider's build replays the consumer's expectations and verifies
   its responses still satisfy them.

3. If the provider makes a change that breaks the contract (drops `balance`),
   the PROVIDER's build fails - before the change ever ships.

Tools: Pact (language-agnostic, the standard) and Spring Cloud Contract (JVM-focused). The magic is the direction - the consumer's real needs drive the test, so the provider learns immediately if a change breaks someone. No more "we removed a field we thought nobody used" incidents. And crucially, it tests the actual running provider, not just a document.

CDC shines between teams you control; it's harder for public APIs

Consumer-driven contracts are ideal for internal microservices where you own both sides (recall the modular monolith → services journey) - each consuming service contributes a contract the provider must honor. For a public API with thousands of anonymous consumers you can't collect contracts from, you can't be consumer-driven; there you lean on strict backward-compatibility discipline, versioning, and provider-published contract tests instead. Match the technique to whether you can enumerate your consumers.

A signed agreement vs. a rehearsal with the other party

OpenAPI is the written contract - a precise, shared document stating exactly what each party will provide. Necessary, but a signed document doesn't prove either side will actually perform: the provider could sign 'I'll deliver X' and then quietly deliver Y. Consumer-driven contract testing is the rehearsal where the consumer shows up and runs through their real requirements against you, and an alarm sounds the instant your performance no longer matches what they need. The consumer says 'in my scene I rely on you handing me the balance,' and if you change the choreography to drop it, the rehearsal fails then and there - not on opening night in front of the audience (production). The document defines the contract; the rehearsal proves you're still honoring it.

Catch the breaking change before it ships

Your accounts service is consumed by three internal services: billing (needs id + balance), notifications (needs id + ownerEmail), and reporting (needs id + createdAt). A developer refactors the accounts response and, thinking it's unused, removes ownerEmail. With OpenAPI docs alone, when would this break be discovered? With consumer- driven contract testing in place, when and how would it be caught?

What does consumer-driven contract testing add beyond an OpenAPI specification?

Key takeaways

  • OpenAPI is a machine-readable API contract that drives interactive docs, client SDK generation, server stubs, and request/response validation.
  • Design-first (write the spec, agree it, then implement) makes the contract the negotiated source of truth; code-first generates the spec from annotations.
  • OpenAPI documents the contract but doesn't prove the running service honors it or that a change won't break a specific consumer.
  • Consumer-driven contract testing captures each consumer's real expectations and verifies the provider against them, failing the provider's build on a breaking change.
  • CDC (Pact, Spring Cloud Contract) is ideal between internal teams you control; for anonymous public consumers, rely on strict backward-compatibility and versioning instead.
War diese Lektion hilfreich?
Diese Seite auf GitHub bearbeiten