skip to content

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

level: middleimportance: must knowfreq 52%

answer

  1. @AutoConfigureStubRunner ids=g:a:v:stubs:port
  2. stubsMode LOCAL / REMOTE / CLASSPATH
  3. downloads stubs JAR → boots WireMock
  4. StubFinder for random ports, StubTrigger for messaging
  5. consumer calls localhost:port like real producer

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.

solid answer

~40 s

Stub 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
java
@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

for a junior

Know @AutoConfigureStubRunner boots a WireMock server from the producer's stub JAR so the consumer calls a fake producer.

for a middle

Explain the ids coordinate format, the three stubsMode options, and StubFinder/StubTrigger helpers.

for a senior

Reason about version pinning vs '+', random-port strategy, and why contract-derived stubs beat hand-written mocks.

for a principal

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.

context