In WireMock, when do you match a peatland survey XML body with matchingXPath() instead of equalToXml()?
answer
- whole document versus one node
- parsed as a tree, not text
- an extra element breaks the strict form
- XPath matches when a node exists
- second argument constrains the selected value
basics
~20 sIn WireMock, equalToXml compares the whole request document, so any element the client adds breaks the stub. WireMock's matchingXPath selects one node instead and matches when it exists. Use XPath when only part of the XML decides the response.
solid answer
~50 sIn WireMock, `equalToXml(` is whole-document matching: the request body is parsed and compared against the expected document you supplied, so formatting is irrelevant but any element the client adds, drops or changes makes the stub miss. That suits a small, fully pinned payload such as the fixed `<carbonReturn>` a peatland carbon-survey client posts to `/carbon/v1/returns`. WireMock's `matchingXPath(` is the surgical alternative: hand it an expression such as `/carbonReturn/siteId` and the pattern matches when the expression selects at least one node, whatever surrounds it. WireMock's two-argument form, `matchingXPath("/carbonReturn/siteId", equalTo("PEAT-114"))`, constrains the selected node's value as well, so unrelated elements may vary freely. Reach for XPath whenever one or two elements decide which canned response a survey return should get. A body that is not well-formed XML at all satisfies neither matcher, because both of them need a parsed document before they can compare anything.
go deeper
Know that WireMock offers two XML body matchers and that both go inside withRequestBody. Be able to say that equalToXml compares the document as a whole and matchingXPath looks at one part of it.
Explain that WireMock parses the body before comparing, so formatting does not matter but structure does, and show the two-argument matchingXPath form that constrains the value of the selected node.
Show the judgment: pick the narrowest matcher that still expresses what the stub is for, and be ready to explain why a whole-document XML matcher becomes a maintenance tax on a payload that changes upstream.
Own the convention across an estate: when whole-document XML equality is allowed at all, who reviews the expected document, and how namespace prefixes are declared consistently so stubs do not silently stop matching.
## Two different questions about the same document WireMock evaluates a request body through a list of body patterns, one per call to `withRequestBody(`, and two of those patterns understand XML. WireMock's `equalToXml(` asks a **whole-document** question: the incoming body and the expected document you supplied are both parsed and compared as trees, so the stub is indifferent to indentation and line breaks but cares about the elements, attributes and text nodes themselves. WireMock's `matchingXPath(` asks a **selective** question: you supply an XPath expression, WireMock evaluates it against the parsed body, and the pattern matches when the expression selects at least one node. The peatland carbon-survey API used throughout this leaf keeps one legacy regulator feed at `POST /carbon/v1/returns`, and that feed still speaks XML: ```xml <carbonReturn> <siteId>PEAT-114</siteId> <peatDepthCm>210</peatDepthCm> <submittedBy>field-team-3</submittedBy> </carbonReturn> ``` ## When the whole-document form is right WireMock's `equalToXml(` earns its place when the payload is small and the whole of it is the point. Signs that you are in that situation: - The document has a handful of elements and a schema that genuinely does not change. - You want the stub itself to be the record of what the client is supposed to send. - Nothing in the payload is generated per run, per device or per clock reading. The cost is that WireMock's `equalToXml(` fails on any structural difference, including one the client added for reasons that have nothing to do with your test: a new `<instrumentSerial>` element, a dropped optional field, or a reordering that the receiving schema treats as significant. Because the comparison is over parsed trees rather than raw text, cosmetic reformatting does not break it, which is the one relief the strict form offers. ## When the XPath form is right WireMock's `matchingXPath(` inverts the default. Instead of describing the whole document you describe the part that decides the outcome: - In WireMock, `matchingXPath("/carbonReturn/siteId")` matches any return that carries a `siteId` element at all. - The two-argument WireMock form, `matchingXPath("/carbonReturn/siteId", equalTo("PEAT-114"))`, matches one site, leaving every other element free to vary. - In WireMock, `matchingXPath("/carbonReturn[peatDepthCm > 200]")` uses an XPath predicate so the stub fires only for deep-peat returns. - WireMock combines several calls to `withRequestBody(` with AND, so two or three narrow XPath patterns can express a precise stub without a single line of expected document. One practical wrinkle: if the regulator feed puts its elements in an XML namespace, a bare path such as `/carbonReturn/siteId` selects nothing, because an unprefixed name in XPath means "no namespace". WireMock's `matchingXPath(` accepts a map of prefixes to namespace URIs for exactly that case, and the symptom of forgetting it is a stub that never matches while the body looks correct. ## How the other servers spell the same job | Server | Whole-document XML | Selective XML | |---|---|---| | WireMock | `equalToXml(` inside `withRequestBody(` | `matchingXPath(`, optionally with a value pattern | | MockServer | an XML body on `request().withBody(...)` | `xpath(` as the body matcher | | Mountebank | `deepEquals` against the request's `body` field | `matches` with a regular expression over the body | The row that matters most is the middle one: MockServer's selective XML matcher is spelled `xpath(`, not WireMock's `matchingXPath(`, and the two are not interchangeable in either direction. ## Choosing, in order 1. Ask what actually decides the response. If it is one element, that element is your matcher and XPath is the tool. 2. If the answer is genuinely "the whole document", check that nothing in it is generated per run. If something is, XPath again. 3. Only then reach for WireMock's `equalToXml(`, and keep the expected document in the mapping file where a reviewer can read it. 4. If the document uses namespaces, declare the prefixes before you blame the expression. ## Mistakes that show up in interviews - Assuming WireMock's `equalToXml(` is a string comparison and worrying about indentation. - Assuming WireMock's `matchingXPath(` needs the rest of the document to match too; it does not. - Expecting a match when the XPath expression selects nothing. An empty node set is a miss, not a vacuous success. - Writing an unprefixed XPath against a namespaced document and concluding the matcher is broken. - Reaching for WireMock's `equalToXml(` on a payload that carries a per-run identifier, then re-editing the stub every time the client regenerates it. The underlying judgment is the same one that governs JSON bodies: a body matcher is an assertion about the request, and an assertion that pins fields nobody cares about buys nothing and costs maintenance. XPath lets you write the narrow assertion; whole-document equality is for the rare case where the whole document really is the contract you are standing in for.
- The regulator's XML puts every element in a namespace. What changes in your WireMock XPath?An unprefixed XPath step only matches elements in no namespace, so `/carbonReturn/siteId` selects nothing. In WireMock you bind prefixes to namespace URIs for the `matchingXPath(` pattern and write the expression with those prefixes. Until you do, the stub never matches and the body looks perfectly correct in the logs, which is why this is a classic wasted afternoon.
- Can one WireMock stub carry both an XML and a JSON body pattern?Mechanically yes, because WireMock keeps every pattern passed to `withRequestBody(` and requires all of them to match. Practically it is a contradiction: a single body is either XML or JSON, so one of the two patterns can never match and the stub is dead. Two stubs on different content types is the honest way to express that.
saying these in an interview costs you the question
- Thinks WireMock's equalToXml compares the raw body text
- Believes matchingXPath needs the rest of the document to match
- Expects a match when the XPath expression selects no node
- Writes an unprefixed XPath against a namespaced document
- Uses equalToXml on a payload carrying a per-run identifier