Contract tests should protect what one system relies on while leaving everything else free to change. Separate transport validity, consumer-specific assumptions, and semantic invariants. Do not encode provider implementation details or demand exact matches for fields the consumer ignores.
Choose the promise under test
An HTTP contract can include method, path, required headers, authentication context, request schema, response status, required fields, and error semantics. An event contract includes topic, key, envelope, schema, ordering scope, delivery expectation, and compatibility rules.
The OpenAPI Specification defines a language-agnostic description for HTTP API operations, parameters, requests, responses, and schemas. Validate producer and consumer artifacts against the schema for broad transport coverage. Schema validation catches a missing required property or wrong type; it does not prove that “total” includes tax or that retries are idempotent.
Write semantic examples for those business promises. A successful cancellation should make later reads show cancelled, and a duplicate event should not create two refunds. These tests may run against a provider environment or a realistic component boundary.
Use consumer-driven contracts selectively
Pact’s consumer-driven contract documentation describes consumer-driven contracts generated from consumer tests and verified by providers. The useful discipline is that each consumer states only the interactions and fields it uses. The provider can change unrelated behavior without breaking that contract.
Keep interactions independent and name their provider state, such as “order 42 exists and is cancellable.” Do not script a long shared scenario whose earlier step must run first. Match variable values by type or constraint where exact equality has no meaning; require exact values where meaning does matter, such as an enum or status.
A consumer contract is not a substitute for authorization, load, resilience, or end-to-end workflow tests. It proves a specific integration assumption.
Handle events differently
An event producer cannot easily verify every consumer at publish time. Store versioned schemas, define backward and forward compatibility, and test representative old consumers against new events. Add fields as optional when possible; do not reuse a field with new semantics merely because its type still matches.
Replay fixtures are valuable for deserializers and projections. Keep sanitized examples from production edge cases and assert durable outcomes. A replay fixture freezes an observed message; pair it with schema and semantic tests so accidental noise does not become permanent contract.
Ordering and delivery belong in the contract. State whether consumers may see duplicates, gaps, reordering, and late events. Include event ID, occurrence time, producer version, and entity version when consumers need idempotency or conflict handling.
Avoid freezing the provider
Do not assert database IDs, header ordering, full JSON equality, internal error text, or fields the consumer never reads. Do not require the provider’s internal sequence of calls. A contract should fail when a supported consumer would break, not whenever the implementation changes.
Version a contract only for an incompatible promise change. Prefer an overlap period in which providers support old and new forms, consumers migrate, and telemetry shows old use has ended. A passing provider verification is necessary before release, but deployment ordering and rollback compatibility still need coordination.
Put contracts in the delivery path
Publish consumer contracts from verified builds, verify them against the exact provider revision, and record the result. Prevent deployment only for contracts belonging to supported consumers and environments; abandoned experimental contracts should expire through explicit ownership rules.
Track provider state setup, test data isolation, nondeterminism, and verification duration. Flaky contracts train teams to bypass the gate.
Inventory one integration and classify each current assertion as transport, semantic, consumer assumption, or implementation detail. Delete or relax the implementation-detail assertions, then add one semantic test for the most damaging compatible-looking change. That produces a smaller suite with a stronger signal.
Sources are linked throughout this guide. Product capabilities can change; consult the linked documentation for your deployment.
Read our editorial approach ↗