In MockServer, what does each VerificationTimes factory assert about how often a request arrived?
answer
- no count means at least once
- six factories, not two
- a ceiling is satisfied by zero
- once and exactly(1) say the same thing
- between covers a timing-dependent poll
basics
~20 sVerificationTimes carries six factories: never, once, exactly, atLeast, atMost and between. Each turns a MockServer verification from at least one matching request into a precise count assertion, so a duplicated or missing hold request fails the test rather than passing.
solid answer
~40 s`VerificationTimes` is the second argument to `MockServerClient.verify`, and it has six factories: `never()`, `once()`, `exactly(int)`, `atLeast(int)`, `atMost(int)` and `between(int, int)`. Without one, the assertion means "at least one matching request arrived", which is the right default for a read the client may cache and the wrong default for anything that changes state. Placing a library hold is not idempotent, so `once()` — or the equivalent `exactly(1)` — is what actually protects you: it fails on the retry that quietly creates a second hold. `atLeast(2)` is the shape for a retry you *expect*, `atMost(1)` for a call that may legitimately be skipped, and `between(1, 3)` for a poll whose exact count depends on timing. WireMock spells the same idea with `exactly(`, `moreThanOrExactly(` and `lessThanOrExactly(`, and has no `never()`.
code
java · 15 linesimport static org.mockserver.model.HttpRequest.request;
import org.mockserver.model.HttpRequest;
import org.mockserver.verify.VerificationTimes;
HttpRequest placeHold = request()
.withMethod("POST")
.withPath("/catalogue/v1/holds");
// the retry policy allows one repeat, never a third attempt
mockServerClient.verify(placeHold, VerificationTimes.between(1, 2));
// and the cancellation path must not have fired at all
mockServerClient.verify(
request().withMethod("DELETE").withPath("/catalogue/v1/holds/H-88213"),
VerificationTimes.never());go deeper
Memorise the six names — never, once, exactly, atLeast, atMost, between — and that leaving the argument off means at least one matching request rather than exactly one.
Explain why atMost passes on zero arrivals, why once and exactly(1) are interchangeable, and which count you would choose for a state-changing hold versus a cacheable availability read.
Show how the count interacts with a retry policy: which counts turn a silent duplicate hold into a failing test, and where an exact count would make a timing-dependent poll flaky.
Set the convention for which outbound calls carry a count assertion at all, so state-changing endpoints are pinned tightly while chatty reads do not generate a stream of timing failures.
## The default is weaker than it looks `MockServerClient.verify(definition)` with no second argument asserts that the journal holds **at least one** request matching the definition. That is a deliberately permissive default, and it is right for reads: an availability lookup the client may cache, retry or batch is rarely interesting to count. It is wrong for anything that changes state. A library hold is created, queued and charged against a patron's borrowing limit; a client that sends it twice has produced a real defect, and the permissive default reports success. ## The six factories `VerificationTimes` is the type of that second argument, and it exposes six static factories. | factory | passes when the journal holds | |---|---| | `VerificationTimes.never()` | zero matching requests | | `VerificationTimes.once()` | exactly one | | `VerificationTimes.exactly(n)` | exactly n | | `VerificationTimes.atLeast(n)` | n or more | | `VerificationTimes.atMost(n)` | n or fewer, including zero | | `VerificationTimes.between(a, b)` | at least a and at most b | Two of them are aliases in effect: `once()` says the same thing as `exactly(1)`, and `never()` the same thing as `exactly(0)`. They exist because the intent reads better, and a reviewer scanning a test understands `once()` faster than a numeric argument. All six are supplied as the second argument to the same `verify` call, so tightening a test from permissive to strict is a one-token edit rather than a restructure. That matters more than it sounds. The usual route to a count assertion is a bug report about a duplicated hold, and the fix should be adding `once()` to an assertion that already exists rather than writing a new test from scratch. It also means the strictness of a whole suite is readable at a glance, because the factory name sits directly beside every request definition it governs. ## Which count fits which catalogue call - `POST /catalogue/v1/holds` — state-changing and not idempotent, so `once()`. A retry that creates a second hold must turn the test red. - `GET /catalogue/v1/titles/{isbn}/availability` — a read. Use `atLeast(1)` if you care that it happened at all, or leave the count off entirely. - `DELETE /catalogue/v1/holds/{holdId}` on a path where cancellation should not occur — `never()`. - A poll whose iteration count depends on timing — `between(1, 5)`, which proves the poll started and caps a runaway loop without pinning a number the scheduler decides. - A call behind a retry policy that permits one repeat — `between(1, 2)`, which fails on a third attempt and on none at all. ## The ceiling that passes on zero `atMost(n)` is the factory that most often surprises people. It asserts "no more than n", and zero is no more than n, so `atMost(1)` passes when the client never called at all. It is a bound on duplication, not evidence of arrival. If you need both — it happened, and it did not happen twice — the honest expressions are `once()`, `between(1, 1)`, or an `atLeast(1)` paired with a separate `atMost(1)`. The same reasoning runs the other way. `atLeast(1)` proves arrival and says nothing about duplication, so it is the wrong instrument for a hold and the right one for a health probe. ## Counts and idempotency The count you choose is really a statement about the endpoint's semantics: 1. Non-idempotent writes get exact counts, because a duplicate is a defect the test exists to catch. 2. Idempotent writes can take a bound, because a retry is harmless and pinning it makes the test track the retry policy rather than the behaviour. 3. Reads usually get arrival only, because counting them couples the test to caching decisions that are free to change. 4. Calls that must not happen get `never()`, and that assertion is only ever as good as the request definition it is built on. ## Pitfalls - Reading a bare `verify` as "exactly once". It is not; it is "at least once", and the difference is the whole class of duplicate-dispatch bugs. - Choosing `exactly(3)` for a poll and inheriting every timing flake the scheduler can produce. - Using `atMost` where arrival matters, and shipping a test that passes when the feature does nothing at all. - Counting retrieved requests by hand in a loop when a `between` or an `atMost` states the same thing declaratively. - Pinning a count on a call the code makes indirectly through a shared HTTP client, where an unrelated feature also contributes requests to the same path.
- Why does VerificationTimes.atMost(1) pass when the client never called at all?`atMost` sets a ceiling, and zero is under every ceiling. It answers "never more than n", which bounds duplication rather than evidencing arrival. If you need both facts, use `once()` or `between(1, 1)`, or pair the ceiling with a separate `atLeast(1)` assertion.
- Is VerificationTimes.once() different from VerificationTimes.exactly(1)?Not in what it asserts — both require exactly one matching request in the journal. `once()` is the readable form for the common case while `exactly(n)` generalises. Reach for `once()` when the intent is "this state-changing call happened one time", which is what most non-idempotent endpoints need.
- How would you assert a catalogue poll that runs an unpredictable number of times?Bound it rather than pinning it: `between(1, 5)` proves the poll started and caps runaway looping, while `exactly(3)` fails whenever the timing shifts. If even a bound proves unstable, assert arrival with `atLeast(1)` and cover the loop's termination condition in a unit test instead.
saying these in an interview costs you the question
- Thinks a bare verify already means exactly once
- Uses atMost to prove a call happened at least once
- Believes MockServer offers only never and once
- Counts retrieved requests by hand instead of using between
- Pins an exact count on a poll whose timing varies