skip to content

Spring Cloud Contract

Contract tests generate producer-side tests from a shared contract and publish stubs consumers test against, so an API change breaks a build rather than production. Interviewers ask how you avoid brittle end-to-end suites, and this is the standard reply.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What problem does Spring Cloud Contract solve, and what is a consumer-driven contract?

level: juniorimportance: must knowfreq 55%

answer

  1. producer verified, consumer stubbed
  2. one contract → tests + WireMock stubs
  3. Groovy/YAML/Kotlin DSL
  4. stubs JAR classifier=stubs
  5. shared source of truth vs drifting mocks

basics

~20 s

Spring Cloud Contract keeps a service (producer) and its callers (consumers) in sync. A contract is a shared file describing a request and the expected response. It generates producer tests and consumer stubs from that one file.

solid answer

~40 s

Spring Cloud Contract does contract testing between an API provider (producer) and its callers (consumers) without a slow end-to-end setup. A 'contract' is a single file (Groovy DSL, YAML, or Kotlin DSL) describing an HTTP request and the expected response. From it the framework does two things: on the producer it generates tests that fail if the real controller stops honoring the contract, and it packages WireMock stubs that consumers run their tests against. 'Consumer-driven' means the contract expresses what consumers actually need, so the producer can't silently break them. It gives you the fast feedback of unit tests with confidence closer to integration tests, and both sides test against the same source of truth instead of hand-written mocks that drift.

code

groovy · 18 lines
groovy
// src/test/resources/contracts/shouldReturnUser.groovy
import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description("should return a user by id")
    request {
        method GET()
        url '/users/42'
    }
    response {
        status OK()  // 200
        headers { contentType(applicationJson()) }
        body([
            id  : 42,
            name: 'Ada'
        ])
    }
}

go deeper

for a junior

Know the one-liner: one contract file produces producer tests + consumer stubs, keeping both sides in sync.

for a middle

Be able to name the DSL formats and describe the two generated artifacts and where they run.

for a senior

Explain 'consumer-driven', the shared-source-of-truth benefit over hand-written mocks, and when contract testing beats E2E.

for a principal

Position SCC vs OpenAPI/Pact, discuss contract ownership/governance, and where it fits in a broader test pyramid strategy.

## The problem In a microservice system, a **producer** exposes an HTTP (or messaging) API and one or more **consumers** call it. Two failure modes are common: (1) the consumer's hand-written mock of the producer drifts from reality, so consumer tests pass but production breaks; (2) the producer changes its API and unknowingly breaks a consumer. Full end-to-end tests catch this but are slow, flaky, and require every service running together. ## Contract testing Spring Cloud Contract (SCC) is Spring's implementation of **Consumer-Driven Contracts (CDC)**. A **contract** is a declarative file that pins down: an incoming **request** (method, URL, headers, body) and the **response** the producer must return (status, headers, body). Contracts are written in a **Groovy DSL**, **YAML**, or a **Kotlin DSL**, and live in the producer repository (typically `src/test/resources/contracts/...`). From that one file, SCC generates two artifacts: 1. **Producer-side generated tests.** The **Spring Cloud Contract Verifier** build plugin (Maven `spring-cloud-contract-maven-plugin` or the Gradle plugin) reads each contract at build time and code-generates a JUnit test. That test fires the contract's request at your running controller (via MockMvc/WebTestClient/RestAssured) and asserts the response matches. If your real implementation diverges from the contract, the build fails. This proves the producer actually satisfies the contract. 2. **Consumer-side stubs.** The same contract is compiled into **WireMock** stub mappings (JSON) and packaged into a **stubs JAR** (Maven classifier `stubs`). Consumers pull that JAR and run their tests against a local WireMock server serving exactly those responses — via **Stub Runner** and `@AutoConfigureStubRunner`. Because the stub is generated from the same contract the producer is tested against, consumer and producer share one source of truth. ## 'Consumer-driven' The contract ideally expresses what consumers need. In practice contracts can live in the producer repo or be contributed by consumers. The key property: the contract is verified on the producer, so a producer change that violates it fails the producer's own build. ## When to use - You own a producer with multiple internal consumers and want to catch breaking changes at build time. - You want fast, isolated tests without spinning up the whole system. Not ideal for public third-party APIs you don't control, or where OpenAPI-based tooling already governs the contract. ## Gotchas - A contract is not a schema; it's a concrete example (one request → one response), optionally with matchers for dynamic parts. - The producer test needs a **base class** to set up the controller under test; forgetting it is the most common first-time failure. - Stubs are only as good as the contracts — an untested field on the producer is invisible to consumers.

  • How is a Spring Cloud Contract different from an OpenAPI/Swagger spec?
    OpenAPI describes the shape/schema of an API for documentation and client generation. A contract is an executable example pair (request→response) that is verified against the running producer and turned into runnable stubs, so it actively fails builds on divergence rather than just documenting.
  • Does the contract file live with the producer or the consumer?
    Most commonly in the producer repo under src/test/resources/contracts, so the producer's build both generates its verification tests and publishes the stub JAR. Alternatively a shared external contracts repo can host them; consumers can also contribute contracts via PR to the producer.

saying these in an interview costs you the question

  • Thinking the contract only documents the API and isn't actually executed/verified.
  • Believing SCC spins up both services together like an end-to-end test.
  • Confusing stubs (consumer side) with the generated verification tests (producer side).

context

open as a page

How does the producer-side test generation work, and what is the base class for?

level: middleimportance: must knowfreq 50%

basics

~20 s

The Verifier build plugin reads each contract and generates a JUnit test that sends the contract's request to your controller and checks the response. You provide a base class that sets up the controller (MockMvc/RestAssured) so the generated test can run.

open as a page

On the consumer side, how does Stub Runner let you test against the producer's stubs?

level: middleimportance: must knowfreq 52%

basics

~20 s

You annotate a consumer test with @AutoConfigureStubRunner, list the producer's stub JAR coordinates, and Stub Runner downloads the JAR and starts a WireMock server serving those stubs. Your consumer code calls it as if it were the real producer.

open as a page

How do you write a contract with dynamic values, and why are body matchers important?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use the DSL's dynamic helpers so the request is matched by a pattern (regex) while the stub returns a concrete example. On the response, a fixed value doubles as both the returned value and the assertion; matchers let you assert by pattern instead of exact value.

open as a page

How do messaging contracts and stub distribution work, and how do you fit Spring Cloud Contract into a CI/CD pipeline?

level: principalimportance: should knowfreq 25%

basics

~20 s

Contracts also cover messaging: they describe an input trigger and an output message on a channel. The producer's build verifies it publishes the right message; consumers use StubTrigger to fire it. In CI, the producer must verify contracts and publish the stubs JAR before consumers pull it, so stub distribution and versioning must be governed.

open as a page