skip to content

In WireMock, how do you build a stubbed reply out of the request that triggered it?

level: middleimportance: must knowfreq 62%

answer

  1. the reply can read its own request
  2. helpers, not string concatenation in Java
  3. path, query, headers, body, method
  4. the payload arrives as raw text
  5. WireMock jsonPath over request.body

basics

~20 s

Attach WireMock's response-template transformer, then reference the request model from the body. WireMock exposes the call as request, so request.path, request.query and request.body are readable, and the jsonPath helper pulls a single field out of a JSON payload.

solid answer

~50 s

With WireMock's `response-template` transformer attached, the Handlebars context carries a `request` object describing the call being answered, and the body is written against it. In WireMock, `{{request.path}}` renders the path without its query string, `{{request.url}}` path plus query, `{{request.method}}` the method, and `{{request.query.depot}}` one query-string parameter. WireMock exposes the payload as raw text in `{{request.body}}`, not as a parsed document, so reaching a JSON field needs the helper: `{{jsonPath request.body '$.routeId'}}`. Values that are not in the request at all can be supplied alongside the stub with WireMock's `withTransformerParameter("centre", "north")` and read as `{{parameters.centre}}`. None of it renders unless WireMock's transformer is attached, so check the response's `transformers` list before you start debugging an expression. One mapping written this way answers every depot and every route on a snowplough dispatch API, and the reply always agrees with the request that produced it.

code

java · 13 lines
java
import static com.github.tomakehurst.wiremock.client.WireMock.*;

stubFor(post(urlPathEqualTo("/dispatch/v1/assignments"))
    .willReturn(aResponse()
        .withStatus(201)
        .withHeader("Content-Type", "application/json")
        .withBody("{"
            + "\"routeId\": \"{{jsonPath request.body '$.routeId'}}\","
            + "\"ploughId\": \"{{jsonPath request.body '$.ploughId'}}\","
            + "\"depot\": \"{{request.query.depot}}\","
            + "\"acceptedPath\": \"{{request.path}}\""
            + "}")
        .withTransformers("response-template")));

go deeper

for a junior

Be ready to name two or three entries of WireMock's request model and to say that the transformer must be attached first. Knowing WireMock's request.path and request.query by name covers most of what is asked.

for a middle

Explain why WireMock exposes the payload as raw text and what the jsonPath helper therefore does, and be able to write a small templated body from memory against a real endpoint.

for a senior

Show judgment about which parts of a reply should be templated at all, and be able to say why a reply that only echoes the request proves very little about the real upstream.

for a principal

Own the convention across suites: how much logic teams may put in a stub body before the mapping stops being readable, and where that logic belongs instead.

## The request model a template can see The point of response templating is that the reply is computed from the call rather than frozen when the stub was written. Once WireMock's `response-template` transformer is attached to a response, the Handlebars context handed to that body contains a `request` object describing the call currently being answered. For a snowplough dispatch API this is what lets one mapping serve `POST /dispatch/v1/assignments` for every depot and every route, echoing back the route the caller actually asked about instead of a hard-coded one. The parts of WireMock's `request` model you reach for most: - `{{request.path}}` — WireMock renders the path of the call, without the query string. - `{{request.url}}` — WireMock renders path and query string exactly as they arrived. - `{{request.method}}` — WireMock renders the HTTP method of the call. - `{{request.query.depot}}` — WireMock renders one named query-string parameter's value. - `{{request.headers.Accept}}` — WireMock renders one named request header's value. - `{{request.body}}` — WireMock renders the request payload, as raw text. That last entry is the one that trips people. WireMock hands the payload over as a string, not as a parsed object, so `{{request.body.routeId}}` does not reach a JSON field. Getting at a field is a helper's job. ## Pulling one field out of a JSON payload WireMock's `jsonPath` helper takes a source and a JSONPath expression, and the source is normally the raw body: ``` {{jsonPath request.body '$.routeId'}} ``` Written into a reply for the snowplough dispatch API, that turns a request of `{"routeId": "R-114", "ploughId": "P-7"}` into a confirmation quoting `R-114` back. The same helper reaches nested fields (`'$.crew.leadOperator'`) and array positions (`'$.passes[0].laneKm'`), so a stub can build a plausible confirmation document out of whatever the client submitted rather than a fixed one. Two properties of the helper are worth internalising: 1. It reads a **source expression**, not a literal string. WireMock treats `request.body` as the source and the quoted JSONPath as the expression evaluated against it. 2. A template problem does not fail the request. WireMock still answers with the status the stub declared; the damage appears in the body, which is why a stub that "returns 201 correctly" can still be wrong. ## Values that are not in the request Some of what a reply needs is neither literal nor derived from the caller — an environment name, a depot code, a base URL. WireMock lets you attach these to the stub itself: - WireMock's `withTransformerParameter("centre", "north")` on the response definition, read in the body as `{{parameters.centre}}`. - The same thing in a WireMock JSON mapping, as a `transformerParameters` object inside the response. This keeps one mapping reusable across environments without turning the body into string concatenation in the test code, and it keeps the varying part visible in the mapping rather than hidden in a Java helper method. ## What this buys, and what it costs | Approach | Reply agrees with the request | Where the reply's shape is visible | |---|---|---| | Literal body per case | only for the exact case you wrote | in the mapping, one file per case | | Templated body reading `request` | for every case the pattern matches | in the mapping, one file for all of them | | Body assembled in test code before stubbing | only for values known before the call | in the test, not in the mapping | The middle row is why templating exists, but it is not free: - A templated reply is harder to read than a literal one, and a mistake in it produces a plausible-looking wrong payload rather than an obvious failure. - A reply that echoes the request cannot contradict the client, so a test asserting "the service returned my route id" proves nothing about the real upstream. - Rendering is work per request, and a body with many helper calls is slower than a fixed payload — usually irrelevant in a test, occasionally relevant on a busy shared instance. The rule of thumb is to template the parts of a reply that genuinely must correlate with the call — identifiers, echoed paths, timestamps — and to leave the rest literal so the mapping still reads like an example of the real payload. ## Getting it right the first time 1. Attach the transformer before anything else. Without WireMock's `response-template` on that response, every expression below is inert text. 2. Decide where each value comes from: the path, the query string, the payload, or a transformer parameter. Mixing these up is the commonest cause of a blank field. 3. Reach into a JSON payload with WireMock's `{{jsonPath request.body '...'}}`, never by dotting into `request.body`. 4. Read the rendered reply once, by hand, against a real request. A field that renders empty looks exactly like a field the upstream genuinely omits.

  • How do you pass a fixed value into a WireMock template without putting it in the request?
    Attach it to the stub with WireMock's `withTransformerParameter("centre", "north")` on the response definition, or a `transformerParameters` object in a JSON mapping, and read it as `{{parameters.centre}}`. The value then travels with the mapping, so the same file works in several environments without the test code assembling the body first.
  • What happens in WireMock when a jsonPath expression matches nothing in the request body?
    The request still gets the status the stub declared — a template problem does not turn into an error response. The damage lands in the payload instead, as an empty or malformed field, which is why an unexpectedly blank value in a stubbed reply should send you to the template before you suspect the client.
  • In WireMock, how do {{request.path}} and {{request.url}} differ?
    In WireMock, `{{request.path}}` renders the path alone and `{{request.url}}` renders the path together with the query string as it arrived. Echoing back the wrong one is a common cause of a stubbed reply that looks nearly right — a confirmation quoting a URL that has silently lost its filters.

saying these in an interview costs you the question

  • Dots into request.body as if WireMock parsed the JSON payload
  • Confuses WireMock's request.query with request.path
  • Assembles the body in test code instead of reading the request
  • Assumes a failed WireMock helper turns the reply into an error status
  • Calls MockServer's Velocity syntax WireMock's template language