On the consumer side, how does Stub Runner let you test against the producer's stubs?
answer
- @AutoConfigureStubRunner ids=g:a:v:stubs:port
- stubsMode LOCAL / REMOTE / CLASSPATH
- downloads stubs JAR → boots WireMock
- StubFinder for random ports, StubTrigger for messaging
- consumer calls localhost:port like real producer
basics
~20 sYou 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.
solid answer
~40 sStub Runner is the consumer-side runtime. In a consumer test you add @AutoConfigureStubRunner with ids like 'com.example:user-service:+:stubs:8100' and a stubsMode. Stub Runner resolves that stubs JAR (LOCAL = local Maven repo, REMOTE = a configured repo, CLASSPATH = from the classpath), unpacks the WireMock stub mappings the producer generated, and boots a WireMock server on the given port. Your consumer's HTTP client points at that port, so it exercises real request/response behavior defined by the contract — no hand-written mock. Because those stubs came from contracts the producer's build verified, the consumer is testing against behavior the producer actually honors. You can inject StubTrigger/StubFinder to fire messaging stubs or look up running ports. This closes the CDC loop: producer verified, consumer integrated against the same contract.
code
java · 18 lines@SpringBootTest
@AutoConfigureStubRunner(
ids = "com.example:user-service:+:stubs:8100",
stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
class UserClientTest {
@Autowired UserClient userClient; // WebClient/Feign pointed at :8100
@Autowired StubFinder stubFinder; // optional: resolve running URL
@Test
void fetchesUserFromStub() {
// Stub Runner already booted WireMock on 8100 serving the
// producer's contract stubs for GET /users/42
User u = userClient.getUser(42L);
assertThat(u.getName()).isEqualTo("Ada");
}
}go deeper
Know @AutoConfigureStubRunner boots a WireMock server from the producer's stub JAR so the consumer calls a fake producer.
Explain the ids coordinate format, the three stubsMode options, and StubFinder/StubTrigger helpers.
Reason about version pinning vs '+', random-port strategy, and why contract-derived stubs beat hand-written mocks.
Design stub distribution (Nexus/Artifactory, CLASSPATH bundling), determinism policy in CI, and messaging-contract consumer verification via StubTrigger.
## Role of Stub Runner The producer's build published a **stubs JAR** (Maven classifier `stubs`) containing **WireMock** stub mappings generated from the contracts. **Stub Runner** is the consumer-side library (`spring-cloud-starter-contract-stub-runner`) that fetches those stubs and runs them so consumer tests hit a fake producer that behaves exactly per contract. ## @AutoConfigureStubRunner On a `@SpringBootTest` (or slice) you add `@AutoConfigureStubRunner`. Key attributes: - `ids` — array of stub coordinates: `groupId:artifactId:version:classifier:port`. Version can be `+` (latest) or a fixed version; classifier is usually `stubs`; port can be fixed or omitted (random, discoverable via `StubFinder`). - `stubsMode` — where to resolve the JAR from: - `StubRunnerProperties.StubsMode.LOCAL` — the local `~/.m2` Maven repository. - `StubRunnerProperties.StubsMode.REMOTE` — a remote repo configured via `repositoryRoot` (Nexus/Artifactory). - `StubRunnerProperties.StubsMode.CLASSPATH` — stubs found on the test classpath (e.g. via a dependency), no download. - `repositoryRoot` — remote repo URL for REMOTE mode. ## What happens at runtime 1. Stub Runner resolves and downloads (or locates) each stubs JAR. 2. It extracts the WireMock JSON mappings. 3. It starts an embedded **WireMock** server per stub artifact on the configured port. 4. Your consumer code (RestTemplate/WebClient/Feign) is pointed at `http://localhost:<port>` and makes real HTTP calls; WireMock replies with the contracted responses. ## Helpers - `StubFinder` (injectable) — `findStubUrl("user-service")` returns the running base URL/port, useful when ports are random. - `StubTrigger` — for **messaging** contracts, triggers the producer's outbound message stub by label so you can verify the consumer's listener. ## Why it's trustworthy The stubs are byproducts of contracts that the **producer's own build verified**. So the consumer isn't testing against a mock someone invented — it's testing against behavior the producer is contractually forced to keep. If the producer breaks the contract, its build fails and the bad stub never ships. ## Gotchas - Coordinates must match the actually published stub artifact (right classifier/version); wrong version → resolution failure. - LOCAL mode requires the stubs JAR installed in `~/.m2` (producer ran `install`/`publishToMavenLocal`); CI typically uses REMOTE. - Fixed ports can collide; prefer random + `StubFinder` for parallel tests. - Stubs reflect only what contracts specify — un-contracted behavior isn't stubbed. - `+` for version pulls latest, which can make tests non-deterministic across releases; pin versions in CI when stability matters. ## When to use Use Stub Runner in any consumer whose behavior depends on a producer you contract-test, to get integration-grade confidence in an isolated, fast test.
- What does stubsMode LOCAL vs REMOTE vs CLASSPATH change?LOCAL resolves the stubs JAR from the local ~/.m2 repo; REMOTE downloads it from a configured repositoryRoot (Nexus/Artifactory); CLASSPATH loads stubs already on the test classpath with no download. CI usually uses REMOTE against the artifact repo.
- How do you handle random stub ports across parallel consumer tests?Omit the port in ids so Stub Runner picks a free one, then inject StubFinder and call findStubUrl("artifactId") to get the actual base URL/port at runtime, avoiding fixed-port collisions.
saying these in an interview costs you the question
- Thinking the consumer needs the real producer service running.
- Believing Stub Runner writes the stubs (the producer generates them; Stub Runner only runs them).
- Not knowing stubsMode / where the stub JAR is resolved from.