Consumer-Driven Contract Testing with Pact / Spring Cloud Contract
Fast recall
Quick revision
Core answer
Modern microservices testing strategies beyond unit and integration tests
Relying only on end-to-end tests (slow, flaky, environment-heavy); writing producer-driven contracts that consumers ignore
Discuss the test pyramid for microservices, consumer-driven vs producer-driven, provider verification, contract broker workflow, breaking change detection, and how contract tests replace many integration tests
Interview-ready guide
Interview answer
Microservices have a testing problem. End-to-end tests are slow, flaky, expensive. Integration tests that mock everything drift from reality. Unit tests miss cross-service API incompatibilities.
Contract testing fills the gap: verify that the producer honors the contract the consumer expects, without running both together.
The flow
- Consumer writes a test describing what it expects from the producer
- The test generates a contract document (JSON file)
- The contract is published to a contract broker like Pact Broker
- Producer's CI pulls contracts from the broker and verifies against its real code
- If the producer changes and breaks a contract, CI fails before deploy
- Consumer's CI uses the contract to generate stubs; tests run against stubs, not real producer
Consumer-driven vs producer-driven
- Consumer-driven (Pact): consumer expresses expectations; producer must honor; catches 'what consumers actually use'
- Producer-driven (Spring Cloud Contract default): producer defines contract; consumers use generated stubs
- Many teams use both depending on service stability
Two dominant tools in 2026
Pact
- Polyglot: JS, Java, Go, Ruby, Python, .NET
- Hosted Pact Broker for contract registry
- Industry standard for cross-team contracts
- can-i-deploy command gates production deploys against all consumer contracts
Spring Cloud Contract
- JVM-focused: Groovy DSL, Maven plugin
- Generates WireMock stubs consumers use
- Tight Spring ecosystem integration
- Contracts packaged as stub JARs in Maven/Artifactory
Implementation pattern (Pact)
Consumer side
- Write test with PactDsl: given state, expect request, define response
- Running the test generates a pact JSON file
- Publish pact to Pact Broker tagged with branch/version
Producer side
- Test annotated @Provider pulls all consumer contracts from broker
- Provider states method seeds DB to make contract scenarios valid
- Test replays each request against real producer; asserts response matches
- Breaking change fails verification
can-i-deploy gate
- Before deploying producer, check broker: 'are all consumers' contracts for this version verified?'
- If yes, safe to deploy; if no, block
Breaking changes contract tests catch
- Renaming fields
- Removing fields consumers use
- Changing field types
- Changing status codes
- Changing content types
Adding fields is non-breaking
- Consumers ignore unknown fields
- Deprecating requires broker to show which consumer versions still use the field
Provider states are critical
- Producer test needs DB in state where GET /users/42 returns valid data
- @State('user 42 exists') method seeds the DB
- Invest in provider state management; otherwise verification fails with 'not found'
Matchers, not exact values
- Use integerType, stringType in contracts
- Verify shape, not specific data
- Data comes from provider states
Async messaging works too
- Pact supports message contracts for Kafka, SQS
- Consumer: 'I expect messages on topic orders with this schema'
- Producer verifies emitted messages match
Test pyramid placement
- Unit tests: business logic, fast and many
- Contract tests: cross-service API compatibility, medium count
- Integration tests: real DB, real message broker, few
- End-to-end: full system smoke tests, very few
- Contract tests eliminate most integration tests for API compatibility
- Don't catch business logic bugs or UX flows
Organizational buy-in required
- Both consumer and producer teams must adopt
- Unilateral consumer contracts without producer verification are useless
- Pact Broker is the coordination point
- CI enforcement on every PR keeps contracts from rotting
vs OpenAPI
- OpenAPI describes API shape; contract tests verify it's honored
- Complementary; Prism, Dredd can do lightweight OpenAPI-based verification
- Not a replacement
The strategic answer
- Contract testing is the essential middle of the test pyramid for microservices
- Consumer-driven catches real consumer dependencies
- Producer-driven is simpler when producer is the stable party
- Pact for polyglot; Spring Cloud Contract for JVM-first teams
- CI gates production deploys via can-i-deploy
- Replaces most end-to-end tests with fast deterministic verification
Consumer-Driven Contract Testing with Pact / Spring Cloud Contract
Direct Answer
Contract testing verifies that a producer service meets the contracts expected by its consumers — without spinning up the full system. Consumer-driven: consumers write expectations (e.g., 'when I call GET /users/42, I expect this JSON shape'); the contract is published to a broker; producer's CI runs the contract as a test against its real code. Breaks fail CI before deploying. Pact (polyglot, pact-broker hosted) and Spring Cloud Contract (Groovy DSL, Maven plugin, WireMock stubs) are the two dominant tools. Solves the 'integration test explosion' problem in microservices: no need for full end-to-end environments for every PR.
Common Mistakes & Pitfalls
Relying only on end-to-end tests (slow, flaky, environment-heavy); writing producer-driven contracts that consumers ignore
Check your understanding
Answer these without looking back at the article.
- How do you handle contract testing for Kafka or async messaging with Pact?
- What's the difference between bi-directional contract testing and traditional Pact?
- How does Confluent Schema Registry enforce message compatibility in Kafka?
- When would you choose Spring Cloud Contract over Pact for a JVM-only team?
Related interview prep categories
2Want a guided curriculum order?
These modules are also sequenced in our flagship backend interview track — open it when you prefer an ordered path instead of browsing by topic.