What does a Spring Cloud Contract stub JAR contain, and how does Stub Runner use it?
answer
- A published artifact, not a running service
- Classifier stubs on the Maven coordinates
- Generated WireMock mappings under META-INF
- Stub Runner boots one server per producer
- CLASSPATH, LOCAL or REMOTE resolution
basics
~20 sA Spring Cloud Contract stub JAR is an ordinary Maven artifact published with the stubs classifier, holding WireMock JSON mappings generated from the producer's contract files. Stub Runner resolves it by coordinates and serves those mappings on a local port.
solid answer
~40 sThe same build that generates provider tests also converts each contract into **WireMock mappings** and packages them into a JAR published beside the producer's application artifact under the `stubs` classifier — coordinates such as `com.archive.digitisation:scan-service:2.14.3:stubs`. Inside, under `META-INF/<groupId>/<artifactId>/<version>/`, sit a `mappings` directory of generated request/response JSON and a `contracts` directory carrying the originals. On the consumer side, `@AutoConfigureStubRunner` names the stubs to run as `groupId:artifactId:version:classifier:port` and a `stubsMode` of `CLASSPATH`, `LOCAL` or `REMOTE` (the last resolving against `repositoryRoot`). Stub Runner downloads the JAR, unpacks the mappings, boots a WireMock server per producer on the requested port, and tears it down after the test. The consumer never runs the producer's code.
code
java · 16 lines@SpringBootTest
@AutoConfigureStubRunner(
ids = "com.archive.digitisation:scan-service:2.14.3:stubs:8347",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "https://artifacts.internal/repository/maven-releases")
class OcrStatusClientTest {
@Autowired
OcrStatusClient client;
@Test
void readsTheStubbedOcrStatus() {
OcrStatus status = client.fetch(8417L);
assertThat(status.state()).isEqualTo("COMPLETED");
}
}go deeper
Recall that the producer publishes a stub JAR and the consumer's test runs against it instead of a deployed service. Knowing that stubs arrive as a normal build artifact is enough at this level.
Be ready to describe the JAR's contents, the stubs classifier, the id form group:artifact:version:classifier:port, and what the three stubsMode values change about where Stub Runner looks.
Expect to justify pinning versus floating stub versions, to explain why the consumer's green test is not evidence about the deployed producer, and to wire stubs into discovery-based clients without test-only branches in production code.
Own the distribution policy: which repository holds stubs, how their retention matches service retention, and whether per-consumer stubs are worth the bookkeeping once one producer has many callers.
## The stub JAR is an ordinary published artifact When the Spring Cloud Contract plugin generates provider tests it also converts the **same** contract files into WireMock stub mappings and packages them into a JAR published alongside the producer's normal application artifact, distinguished by the `stubs` classifier. Coordinates look like `com.archive.digitisation:scan-service:2.14.3:stubs`. There is no bespoke protocol, no contract registry service and no daemon: whatever Maven-compatible repository the organisation already runs — Nexus, Artifactory, or a developer's local `~/.m2` during a debugging session — is the distribution channel. That is a deliberate design choice worth saying out loud in an interview. Stubs travel on the same rails as the code, get the same version number, and are pruned by the same retention policy. If you can deploy `scan-service` `2.14.3`, you can get the stubs for exactly `2.14.3`. ## What is inside Unpack one and you find a tree keyed by the producer's own coordinates: - `META-INF/<groupId>/<artifactId>/<version>/mappings/` — one WireMock JSON mapping per contract: a request pattern, and the canned response to return when a request matches it. - `META-INF/<groupId>/<artifactId>/<version>/contracts/` — the original contract files, carried along so tooling and humans can see what the mappings were generated from. The crucial word is **generated**. These are not WireMock files a consumer author wrote by hand from reading a wiki page. The same source file that produced these mappings also produced the provider tests that must pass on the producer's build, which is the only reason to trust the stub at all. ## How Stub Runner locates one On the consumer side, `@AutoConfigureStubRunner` declares which stubs to run. The `ids` entries use the form `groupId:artifactId:version:classifier:port`, and `+` may stand in for the version to take the latest available. `stubsMode` decides where resolution happens: | `stubsMode` | Where Stub Runner looks | Typical use | |---|---|---| | `CLASSPATH` | the test classpath | stub JARs declared as ordinary test dependencies | | `LOCAL` | the local Maven repository | producer just built and installed on the same machine | | `REMOTE` | the repository named by `repositoryRoot` | CI resolving from the shared artifact repository | Pinning matters here. `+` gives you the newest stubs and therefore the fastest signal that the producer moved; a pinned version gives you a reproducible build and defers that signal. Teams often pin on release branches and float on trunk. ## What Stub Runner does at test time 1. Resolves each declared id to a stub JAR through the chosen mode. 2. Unpacks the `mappings` for that producer's coordinates. 3. Starts a **WireMock server per producer**, on the port named in the id or on a random free port. 4. Loads the mappings into it, so a matching request gets the contract's canned response and an unmatched one does not. 5. Exposes the resolved ports to the test, and can register each stubbed producer with the test's service-discovery client so a client that resolves the producer by service name reaches the stub instead. 6. Shuts the servers down when the test context closes. An archive-digitisation workflow team consuming `scan-service` therefore writes an ordinary Spring Boot test, points its HTTP client at the port Stub Runner reports, and exercises its own retry, mapping and error-handling code against responses that came out of the producer's contract file — with the producer's application, database and message broker all absent. ## What this buys, and what it does not - **Buys:** a consumer test that runs in seconds with no producer deployment, no shared environment, and no hand-maintained fixture drifting quietly away from the real API. - **Buys:** a single source of truth. The stub cannot describe a response the producer's own generated test does not also assert, because both came out of the same file. - **Does not buy:** proof that the producer still behaves this way. Running against the stub only shows your client copes with the contract's shape. The evidence that the deployed producer matches that shape is the producer's generated test going green on the producer's build — a different build, on a different repository. - **Does not buy:** freshness for free. A consumer pinned to `2.14.3` keeps passing against `2.14.3` stubs long after the producer has published `2.19.0`. Whoever chose the pin owns that gap. The interview-grade summary: the stub JAR is how a producer-authored contract becomes something a consumer's test can actually run against, distributed as a plain build artifact and served locally by WireMock through Stub Runner.
- The consumer's client resolves the producer by service name rather than a URL. How does the test still reach the stub?Stub Runner can register each stubbed producer with the test's service-discovery client, so a name lookup returns the local WireMock port instead of a real instance. The client code is left untouched — no test-only URL property, no conditional wiring — which is what keeps the test honest about how the client behaves in production.
- What stops the stub a consumer runs against from disagreeing with what the producer actually serves?Nothing on the consumer side — and that is the point. Both the stub mappings and the producer's generated tests come out of the same contract file, so the guarantee is enforced on the producer's build: if the producer's code stops matching the contract, its own build fails before that version is ever published as stubs.
- Can two different consumers of the same producer be served different subsets of its stubs?Yes. Spring Cloud Contract supports a per-consumer mode where contracts declare which consumer they belong to and Stub Runner serves only that consumer's subset. It is worth turning on when one producer has many callers, because it keeps a consumer's test from silently depending on an interaction some other team negotiated.
saying these in an interview costs you the question
- Thinks Stub Runner starts the real producer application
- Says the consumer's test run generates the stub JAR
- Cannot say where stub JARs are published
- Believes the WireMock mappings are hand-written
- Claims passing against stubs proves the producer still matches