In MockServer, does an expectation naming one query-string parameter still match a request that sends three?
answer
- subset, not equality
- unnamed entries are simply ignored
- clients add headers you never wrote
- exclusion has to be written down
basics
~20 sYes. MockServer matches on a subset, not on equality. Every header, cookie and query-string parameter you name is a constraint the request must satisfy; every one you do not name is ignored, however many the client sends.
solid answer
~40 sYes, and it is the single most useful thing to know about MockServer's request side. MockServer's `request().withPath("/v1/sweeps").withQueryStringParameter("borough", "camden")` says *the call must carry a borough parameter matching camden*; it says nothing about `trade`, `from` or `page`, so `GET /v1/sweeps?borough=camden&trade=survey&page=3` matches. The same subset rule governs headers and cookies in MockServer, which matters because HTTP clients add fields you never wrote — a user agent, an encoding field, a correlation identifier — and none of them can prevent a match. The practical consequence is that an expectation is almost always broader than its author intended. If you need something to be *absent*, or present with a value you do not want, MockServer makes you say so explicitly with its `NottableString.not(...)`; there is no implicit exclusivity.
go deeper
Remember the direction of the check: a MockServer expectation lists what the call must have, and anything it does not mention is allowed. Extra query parameters and extra headers never break a match.
Be able to walk through several concrete calls and say which the MockServer expectation answers and why, and to name the mechanism, its NottableString negation, that turns silence into an explicit prohibition.
Demonstrate the review habit. For any MockServer expectation, state what else gets through it, and treat a stub that answers a call the feature should never make as a defect even while every test is green.
Set the team convention for how tightly stub definitions are written, and weigh it honestly: over-broad predicates hide wrong calls, while over-tight ones break on every harmless client change.
## What "match" means on MockServer's request side A MockServer expectation is registered with `client.when(request()...)`, and that MockServer `request()` object is a **set of constraints**, not a description of a whole message. For each entry you wrote, MockServer asks: is there something in the incoming call that satisfies this? It never asks the reverse question — was there something in the incoming call I was not told about? That second question has no place in MockServer's model, which is why headers, cookies and query-string parameters you did not name cannot influence the outcome. So a MockServer expectation carrying `withPath("/v1/sweeps")` and `withQueryStringParameter("borough", "camden")` constrains exactly two things and is silent about everything else the client may have sent. ## The chimney-sweep example, spelled out | incoming call to the MockServer sweep stub | matched? | why | |---|---|---| | `GET /v1/sweeps?borough=camden` | yes | both MockServer constraints are satisfied | | `GET /v1/sweeps?borough=camden&trade=survey&page=3` | yes | `trade` and `page` were never constrained | | `GET /v1/sweeps?trade=sweep` | no | nothing satisfies the `borough` constraint | | `GET /v1/sweeps?borough=hackney` | no | `borough` is present, its value is not | The second row is the one people get wrong. Extra parameters are not evidence against a match in MockServer; they are simply not part of the question being asked. ## Headers and cookies are where it actually surprises people - **HTTP clients add fields you never wrote.** A user-agent field, an encoding field, a connection field, a correlation identifier injected by a tracing library — none of them can stop a MockServer expectation from matching, because the expectation says nothing about them. - **An authenticated-path stub answers unauthenticated calls.** If a MockServer expectation names a booking path but not `X-Sweep-Tenant`, it will happily answer a call carrying no tenant header at all, because "the tenant header must be there" was never asserted. - **A stray cookie is harmless.** A `sweepSession` cookie the browser happens to carry does not disturb a MockServer expectation that mentions no cookies. - **The predicate is almost always broader than its author intended.** The set of calls a MockServer expectation answers is the set that satisfies what you wrote, which is usually far larger than the one example you had in mind while writing it. ## Saying "not this" explicitly MockServer gives you negation rather than implicit exclusivity, and it lives on MockServer's `NottableString`: - MockServer's `NottableString.not("page")` applied to a **name** means no entry of that name may be present. - MockServer's `NottableString.not("hackney")` applied to a **value** means the entry is there with something else. - In a MockServer JSON expectation the same negation is written as a `!` prefix on the name or the value rather than as a call, which is worth recognising when you read a definition file somebody else wrote. - Adding more positive constraints narrows a MockServer expectation only in the directions you name; it never forbids anything you did not mention. The practical rule is that if a test's meaning depends on something being absent — no `page` parameter, no tenant header, no session cookie — that absence has to be written down. Silence in a MockServer expectation means "I don't care", never "must not be there". ## Where the other stub servers land In WireMock the model is the same, since a WireMock request pattern also constrains only what it names, and WireMock's `noValues()` matcher exists precisely because saying nothing about a parameter is not the same as requiring it to carry no values, with WireMock's `absent()` covering the missing case. Mountebank is the interesting exception in this group. Mountebank's `equals` predicate compares only the fields you list, exactly as the other two do, but Mountebank's `deepEquals` compares the whole object, so a Mountebank `deepEquals` predicate over a `query` object stops matching the moment the client adds a parameter you did not list — the one place in these three products where an unmentioned field is fatal by default. ## Reviewing a predicate in three passes 1. Read each entry of the MockServer expectation as a constraint and ask what it does **not** say. That is the honest description of its reach. 2. List the fields the real client sends that the expectation is silent about. Every one of them is currently permitted, in any value. 3. Decide per field whether silence is right. Where it is not, add a positive MockServer constraint or an explicit MockServer `not(...)` — and prefer that to lengthening the path, which narrows a different axis and leaves the original hole open. ## Why this is worth knowing cold Almost every "my stub matched the wrong call" report on a MockServer-backed suite reduces to this rule. The expectation was read as a description of one specific request when it is really a filter, and the filter was coarser than the reader assumed. Once you internalise that MockServer matches on a subset, the second question becomes automatic — what else gets through here? — and that is the question worth asking in review, because a MockServer expectation that is too broad still returns 200 and still keeps the suite green.
- How would you make that same MockServer expectation match only when no `page` parameter is present?Negate the name rather than the value. In MockServer a query-string entry built from `NottableString.not("page")` matches only calls carrying no parameter of that name, so you add it alongside the borough constraint. Leaving `page` unmentioned does the opposite of what people expect: it permits the parameter with any value, because silence in a MockServer expectation means the field was never part of the question.
- Does the subset rule still hold for a MockServer expectation written as JSON rather than in Java?Yes — the JSON form is the same MockServer expectation model serialised, so headers, cookies and query-string parameters you list are constraints and everything you omit is unconstrained. The negation you would write as MockServer's `NottableString.not(...)` in Java appears as a `!` prefix on the name or the value in its JSON form. Nothing about matching breadth changes with the authoring format.
saying these in an interview costs you the question
- Assumes a MockServer expectation must list every parameter the request sends
- Thinks one extra query parameter makes an expectation stop matching
- Expects a stray user-agent header to prevent a match
- Believes naming one parameter implicitly forbids all the others
- Narrows a too-broad predicate by lengthening the path instead of negating