In WireMock 4, why does an embedded fixture's call to stubMapping.setRequest() no longer compile?
answer
- the data model changed shape
- value objects, not mutable beans
- builders arrived with WireMock 4
- derive a copy, do not mutate
- the method you want is transform
basics
~20 sWireMock 4 made its core data classes immutable and gave them builders, so the in-place setters on StubMapping are gone. Instead derive a new mapping: WireMock 4's StubMapping.transform takes a lambda that sets the request on a builder.
solid answer
~40 sWireMock 4 turned the core data model **immutable**: `StubMapping`, `Metadata`, `Parameters`, `GlobalSettings`, `ResponseDefinition` and `RequestPattern` are now value objects built through builders, so the mutating setters that fixture code used to call on a live instance were removed. That is why `stubMapping.setRequest(pattern)` stops compiling the moment an embedded fixture is upgraded — it is not a missing dependency and not a renamed method, it is a deliberate shape change. WireMock 4's replacement is `transform`: `oldStubMapping.transform(b -> b.setRequest(pattern))` hands you a builder seeded from the existing mapping, applies your change, and returns a **new** `StubMapping`. Your fixture then has to do something with that returned object, because the original is untouched. Code that mutated in place and relied on the change being visible through every reference needs restructuring, not a one-line substitution.
code
java · 13 linesWireMockServer ladderStub = new WireMockServer(wireMockConfig().dynamicPort());
ladderStub.start();
ladderStub.stubFor(get(urlPathEqualTo("/ladders/tayside-a/standings"))
.willReturn(aResponse()
.withStatus(200)
.withBody("[{\"rink\":\"Frost\",\"points\":14}]")));
// WireMock 4: StubMapping is immutable, so the in-place setter is gone.
// original.setRequest(widerPattern); // no longer compiles
// Derive a new mapping from the old one instead:
StubMapping widened = original.transform(b -> b.setRequest(widerPattern));go deeper
Know that WireMock's stub mappings are objects your fixture can hold, and that in WireMock 4 you produce a changed copy rather than editing the one you already have.
Explain what immutability with builders means for a data class, and be able to write the WireMock transform call that produces a modified StubMapping from an existing one.
Diagnose this quickly during an upgrade: recognise a removed setter as a deliberate model change, and restructure fixture code that assumed a mutation would be visible through every reference.
Decide how a fleet of suites absorbs a breaking model change — a shared fixture library that hides it, one coordinated upgrade, or a window where both shapes coexist — and defend the cost of whichever you pick.
## Where this bites an embedded fixture Embedded fixtures grow helpers. A suite that stubs a curling-club ladder API starts with one stub for `GET /ladders/tayside-a/standings`, then someone writes a helper that takes a mapping the fixture already built and adjusts it — widening a matcher, tightening a header, swapping a body for a different rink table. On the previous major line of WireMock that helper was written the obvious way: hold the `StubMapping`, call a setter on it, carry on. Upgrade the fixture to WireMock 4 and that helper stops compiling. The method it calls is not deprecated, not renamed, not moved — it is gone. This is the moment the question is really about, because the reflex diagnosis ("my dependencies are mismatched") is wrong and sends people looking in the build file for an hour. ## What WireMock 4 changed WireMock 4 made the **core data classes immutable and gave them builders**. The classes affected are the ones fixture and extension code holds most often: | WireMock 4 core class | what it is | |---|---| | `StubMapping` | the mapping object itself | | `RequestPattern` | the matching half of a mapping | | `ResponseDefinition` | the reply half | | `Metadata` | the arbitrary data attached to a mapping | | `Parameters` | the parameter bag passed around by extensions | | `GlobalSettings` | the server-wide settings object | An immutable value object has no in-place setters, by definition. You do not change one; you produce another one that differs in the way you want. That is the whole of the change, and everything else follows from it. ## The replacement: transform WireMock 4's derivation method is `transform`. It takes a lambda that receives a builder already seeded from the existing instance, lets you apply your change to that builder, and returns a **new** instance: ``` StubMapping widened = original.transform(b -> b.setRequest(widerPattern)); ``` Read that carefully, because the shape is the answer to the question: 1. `original` is unchanged when the call returns. Nothing about it was touched. 2. The lambda's `b` is a builder, not the mapping — `setRequest` there is a builder call, which is why the name survives even though the instance method did not. 3. `widened` is a distinct object, and it is the only thing that carries your change. ## The mistake the compiler will not catch The compile error is loud and easy. The follow-on defect is quiet. Code written for the old setter almost always called it **for its side effect** and then carried on using the original reference. Substituting `transform` mechanically — writing the call, ignoring the return value or assigning it to a variable nobody reads — compiles cleanly and does nothing at all. The fixture starts, the stub is registered in its original form, the test fails against a mapping that looks right in the source and is wrong in the server. So the migration is not textual. For each call site you have to answer: **who was holding the mapping, and what now holds the derived one instead?** Sometimes that is a local variable and the fix is trivial. Sometimes it is a field that several tests share, and the honest fix is to build the mapping you want in one place rather than mutating a shared one. ## Why the change is a good one It is worth being able to say why this happened rather than only that it did: - A mapping that cannot change underneath you is safe to hold, pass around and compare. - A builder makes the set of legal states explicit, instead of leaving an object half-configured between two setter calls. - Fixture helpers become functions — mapping in, mapping out — which is far easier to reason about than a sequence of mutations whose order matters. An interviewer asking this is usually probing whether you recognise a deliberate API-shape change when you see one, or whether you treat every compile failure after an upgrade as somebody else's bug. ## What to say in the interview - Name the change: the core data classes became immutable value objects with builders. - Name WireMock 4's replacement precisely: `oldStubMapping.transform(b -> b.setRequest(x))`. - Say that `transform` **returns** a new mapping and leaves the original alone. - Add the trap: a mechanical substitution that discards the return value compiles and silently does nothing. - Note that the same change lands on several classes at once, which is itself the clue that you are looking at a model change and not a single removed method. That last point is the one that separates a candidate who has done the upgrade from one who has read about it. When a WireMock fixture breaks on `StubMapping` it usually breaks on `ResponseDefinition` and `RequestPattern` in the same afternoon, and the person who has lived through it says so before being asked.
- Which other WireMock 4 classes became immutable alongside StubMapping?The same WireMock 4 change covers `Metadata`, `Parameters`, `GlobalSettings`, `ResponseDefinition` and `RequestPattern`. They are the core data classes fixture and extension code used to mutate directly, and they all build through a builder now. If an upgrade breaks on one of them it usually breaks on several, which is the hint that you are looking at a model change rather than a single removed method.
- What is the practical risk of treating transform as a drop-in for the old setter?`transform` returns a new object and leaves the original alone. Code written for the old setter often called it purely for the side effect and then kept using the original reference, so a mechanical substitution compiles and silently changes nothing. The fix is to follow the returned value through: whatever held the old mapping has to hold the derived one instead.
saying these in an interview costs you the question
- Assumes the method was renamed rather than removed
- Thinks transform mutates the mapping it is called on
- Blames a dependency mismatch instead of the API change
- Expects the derived mapping to take effect automatically
- Believes only WireMock's StubMapping became immutable