skip to content

In a Spring Cloud Contract DSL file, which parts drive the generated test and which drive the stub?

level: seniorimportance: nice to knowfreq 29%

answer

  1. The same file feeds two different generators
  2. One side becomes the stub, one the test
  3. consumer(...) always ends up in the stub
  4. Request loose, response concrete for stubs
  5. bodyMatchers states the tolerance explicitly

basics

~20 s

One Spring Cloud Contract file compiles into two artefacts. Values wrapped in consumer(...) go into the WireMock stub; values wrapped in producer(...) go into the generated provider test. That lets the stub match loosely while the test asserts precisely.

solid answer

~50 s

A single contract file feeds two generators, and dynamic values say which output each half is for: `value(consumer(x), producer(y))` — `$()` is the shorthand — puts `x` in the **stub mappings** and `y` in the **generated provider test**. The asymmetry is exactly inverted between the two blocks. In `request`, the consumer side is usually a `regex(...)` so the stub matches any caller's URL or payload, while the producer side is a concrete value the generated test actually sends. In `response`, the consumer side is a concrete body — a stub must return real data — while the producer side is a matcher the generated test asserts against whatever the live service returns. `bodyMatchers` states the same thing explicitly, pairing a JSON path with `byRegex`, `byType` and friends. Getting the sides backwards produces a stub that returns a regex string.

code

groovy · 22 lines
groovy
import org.springframework.cloud.contract.spec.Contract

Contract.make {
    request {
        method 'GET'
        url value(
            consumer(regex('/scans/[0-9]+/ocr-status')),
            producer('/scans/8417/ocr-status')
        )
    }
    response {
        status 200
        headers {
            contentType applicationJson()
        }
        body(
            scanId: value(consumer(8417), producer(regex('[0-9]{1,10}'))),
            state: 'COMPLETED',
            confidence: value(consumer(0.9731), producer(regex('0[.][0-9]{1,6}')))
        )
    }
}

go deeper

for a junior

Recall that one Spring Cloud Contract file produces both the stub consumers run against and the test the producer must pass. Knowing there are two outputs is enough before you start authoring contracts.

for a middle

Be ready to read a value(consumer(...), producer(...)) field aloud and say which generator each half feeds, and to explain why a stub needs a concrete response body while the generated test needs a matcher.

for a senior

Expect to review a contract and spot the inverted side, the over-literal payload and the fixture the producer half assumes. Explain the failure each one produces and which half of the pipeline reports it.

for a principal

Own the tolerance policy across an estate: which field kinds get byType() by default, where exact equality is mandatory because consumers branch on the value, and how that convention keeps producer builds from failing for non-contract reasons.

## One file, two outputs The thing that makes Spring Cloud Contract's DSL worth studying is that a contract file is not read once. The build plugin runs it through two generators: - a **provider-test generator**, which emits a JUnit test that sends the request at the real application and asserts the response; - a **stub generator**, which emits WireMock mappings packaged into the stub JAR that consumers run against. Both outputs come from the same source, which is the entire guarantee the tool offers: the shape a consumer develops against and the shape the producer is tested on cannot drift apart, because there is only one file. But the two outputs need *opposite* things from the same field, and that is what the DSL's dynamic values exist to express. ## The two sides of a dynamic value Write a field as `value(consumer(a), producer(b))` — or with the `$(...)` shorthand — and you are addressing the two generators separately. The rule is mechanical and never varies: - everything on the **`consumer` side goes into the stub**; - everything on the **`producer` side goes into the generated provider test**. (`client`/`server` are aliases for the same two sides.) What changes is which of those wants a pattern and which wants a literal, and that flips between the request and the response: | Block | `consumer(...)` side, which becomes the stub | `producer(...)` side, which becomes the generated test | |---|---|---| | `request` | the **pattern** the stub will match, so any caller with a plausible URL or payload gets a response | the **concrete request** the generated test actually sends at the real service | | `response` | the **concrete body** the stub returns, because a stub has to hand back real data | the **matcher** the generated test asserts with, so the live service may return any conforming value | ## Why the inversion is the point Look at what each output is *for* and the flip stops being arbitrary. A **stub** stands in front of the consumer's code. It must be **permissive about what it is asked** — the consumer under test will invent its own scan ids, correlation headers and timestamps, and a stub that only matched one hard-coded URL would be useless. It must be **concrete about what it answers**, because the consumer's parsing, mapping and error-handling code needs a real payload to work on. A **generated provider test** stands in front of the real service. It must be **concrete about what it sends** — a request has to be a real request, and the base class has seeded exactly one fixture for it. It must be **permissive about what it accepts back**, because the real service will return a generated id, a current timestamp, or a value that legitimately varies between runs; pinning those literally would make the producer's build fail for reasons that are not contract breaks. So: loose input plus tight output for the stub, tight input plus loose output for the test — and one file expresses both. ## Saying it explicitly with `bodyMatchers` Wrapping every field gets noisy on a large payload. `bodyMatchers` separates the *example* body from the *matching rules*, addressing fields by JSON path: - `byRegex(...)` — the value must match a pattern; - `byType()` — only the JSON type has to agree, which is the usual choice for ids and free text; - `byEquality()` — the value must be exactly what the example shows, which is right for enums and status codes. The example body still supplies the concrete data the stub returns; the matchers supply the tolerance the generated provider test applies. Same duality, stated in two places instead of inline. ## Getting the sides backwards Three failure modes come up in real reviews, and recognising them is the senior-level signal: 1. **A regex on the consumer side of a `response`.** The stub dutifully returns the pattern *as a string literal*, and the consumer's deserialiser blows up on `[0-9]+` where it wanted a number. The symptom looks like a consumer bug and is a contract bug. 2. **A literal on the producer side of a `request` that the fixture cannot serve.** The generated test sends a path the base class never seeded, gets a 404, and the failure reads as a contract break when it is a data gap. 3. **Both sides literal everywhere.** Perfectly correct, and perfectly brittle: the stub then matches only that one exact request, so a consumer that increments an id gets no match, and the producer's test pins a generated value that changes every run. An archive-digitisation workflow team pinning `scanId: 8417` on both sides of a response is the classic case — 6 consumer teams then write tests that only work for scan 8417, and the producer's build fails the first time the service returns a database-assigned id. One `byType()` fixes both halves at once, which is exactly why the duality is worth understanding rather than copying.

  • Which side of a response field should carry the regex, and what breaks if you swap them?
    The `producer` side carries the regex, because the generated test must tolerate a value the live service computes. Swap them and the stub returns the pattern verbatim as a string — consumers get `[0-9]+` where they expected a number — while the generated test pins one literal id and fails the moment the database assigns a different one.
  • When is a plain literal on both sides the right choice?
    Whenever the value genuinely is fixed: enum members, status strings, content types, a version discriminator. A literal makes the stub return the real token and the generated test assert it exactly, which is what you want for anything the consumer branches on. Reach for a matcher only where the value is generated, timestamped or otherwise free to vary.
  • What does `bodyMatchers` give you that wrapping each value inline does not?
    It separates the example payload from the matching rules, so a large body stays readable, and it addresses fields by JSON path — which is how you reach into arrays and nested structures without restructuring the example. It also makes the tolerance policy reviewable in one block instead of scattered across dozens of fields.

The contract is a two-sided mould: the stub is cast from the consumer half, the provider test from the producer half, and the same cavity shapes both.

saying these in an interview costs you the question

  • Thinks consumer(...) values end up in the generated test
  • Puts a regex where the stub must return a value
  • Believes one contract file produces only the provider test
  • Cannot explain why request and response are inverted
  • Treats every field as a literal, so generated ids break