Contract Tests
Does service A continue satisfying the assumptions of service B? Contract tests are a sensor of boundary assumptions — the implicit agreements between independent components about what the interface between them promises.
Contracts measure boundary assumptions
A contract is a specification of what one component promises to provide and what another component is allowed to assume. Contract tests verify that both sides of the boundary continue to honor these promises as the system evolves.
# Consumer-driven contract
Service B (consumer) defines:
"I expect GET /users/{id} to return
{ id: int, name: string, active: bool }"
Service A (provider) must satisfy:
its response matches this shape
the field types are correct
the endpoint exists and responds
The sensor detects breaking changes — when a provider modifies its API in a way that violates the assumptions its consumers depend on. This is particularly powerful in microservice architectures where independent teams deploy independently.
Contracts measure boundary assumptions. Types measure a particular class of structural inconsistency. Contract tests are the boundary-level version of what type checkers are at the module level: both enforce structural promises, but at different scopes and different feedback latencies.
In practice
A reading is a pact verification failure that names the broken clause and the consumer it belongs to:
Verifying a pact between Orders (consumer) and Billing (provider)
A request for invoice 42
GET /invoices/42
returns a response which
has status code 200
includes headers
"Content-Type" with value "application/json" FAILED
actual value: "text/html"
has a matching body FAILED
$.amount: expected number, got string
2 interactions, 2 failures
Reading it well:
- Read the clause, not just the red. Status, header, and body failures have different owners and different fixes. The clause names which promise broke.
- The failure belongs to the change, not the expectation. The consumer stated its assumption first; the provider moved. The fix direction is usually to hold the provider to the promise, not to relax the expectation.
- A passing pact is one-directional evidence. It says the provider honors the recorded expectations. It does not say the expectations are complete: a consumer that never wrote a pact is a gap the sensor cannot see.
How it gets gamed
- Delete or weaken the pact. Removing an expectation the provider now violates turns red to green without fixing the provider. The consumer still holds the old assumption, so the breakage moves from the report to production.
- Verify against a stub. Running the provider side of a pact against a mock instead of the real service passes the check and tests nothing.
- Let pacts go stale. A pact not re-verified after a provider change is an assumption wearing a check mark.
The meta-signal is the pact count per consumer over time. A pact file that shrinks after a failure is the sensor being overridden.
Response playbook
When a contract test fails:
- Read the broken clause. Status, header, and body failures have different owners. The clause names which promise broke and which consumer depends on it.
- Assume the provider is wrong until shown otherwise. The consumer stated its assumption before the provider changed. The default fix is restoring the promise, not relaxing the pact.
- If the consumer is wrong, change both sides together. Update the pact and the consumer in the same change, so the window where neither matches is zero.
- Check the other consumers. A broken clause usually breaks every consumer that holds it. The report names one; the pact broker lists the rest.
What it cannot detect
Contract tests verify the shape of communication, not its meaning. A response that has the right fields and types but contains wrong values will pass a contract test. They also cannot detect integration failures that emerge from the interaction of correct components producing incorrect emergent behavior.
Related sensors
References
Publications
- I Depended on You and You Broke Me: An Empirical Study of Manifesting Breaking Changes in Client Packages —
- Design, Monitoring, and Testing of Microservices Systems: The Practitioners' Perspective —
- Consumer-Driven Contracts: A Service Evolution Pattern —
Tooling
- PactConsumer-driven contract testing
- Spring Cloud ContractContract testing for Spring/JVM
- PostmanAPI testing and contract validation