skip to content

You need to prove that your code invoked a particular static factory method exactly once with a given argument, and never invoked another static on that same class. How is that verification expressed with Mockito's static mocking, and how does it differ from verifying an ordinary mock?

level: middleimportance: should knowfreq 32%

answer

  1. handle.verify(() -> Type.call(args), mode)
  2. default mode is times(1)
  3. matchers + captors work inside the lambda
  4. verifyNoMoreInteractions scoped to that class
  5. verify before close(), same thread only

basics

~20 s

Verification goes through the MockedStatic handle: mocked.verify(() -> Factory.create("id")) with an optional mode such as times(2) or never(), plus mocked.verifyNoMoreInteractions(). The invocation is described by a lambda instead of by calling a method on a mock instance.

solid answer

~40 s

Because there is no mock instance, the `MockedStatic` handle plays that role: ```java try (MockedStatic<Factory> f = mockStatic(Factory.class)) { service.run(); f.verify(() -> Factory.create("id")); // times(1) by default f.verify(() -> Factory.create(anyString()), times(1)); f.verify(Factory::shutdown, never()); f.verifyNoMoreInteractions(); } ``` The lambda argument is a `MockedStatic.Verification` — Mockito executes it in verification mode and matches the recorded invocation. All the usual modes work: `times`, `never`, `atLeast`, `atMostOnce`, `timeout`. Argument matchers and `ArgumentCaptor` work inside the lambda exactly as in instance verification, and the all-or-nothing matcher rule still applies. Differences worth naming: verification must happen while the scope is open; it covers only calls on the registering thread; and `verifyNoMoreInteractions()` on the handle is scoped to that class's statics.

code

java · 10 lines
java
try (MockedStatic<Metrics> metrics = Mockito.mockStatic(Metrics.class)) {
    ArgumentCaptor<String> name = ArgumentCaptor.forClass(String.class);

    service.handle(request);

    metrics.verify(() -> Metrics.increment(name.capture()), times(1));
    assertEquals("requests.handled", name.getValue());
    metrics.verify(() -> Metrics.increment("requests.failed"), never());
    metrics.verifyNoMoreInteractions();
}

go deeper

for a junior

Recall the handle-based verify syntax with a lambda and that times(1) is the default mode.

for a middle

Use modes, matchers and captors correctly, and know that verification must happen inside the open scope.

for a senior

Explain the thread-locality trap behind false 'wanted but not invoked' failures and prefer outcome assertions over interaction assertions where possible.

for a principal

Argue about coupling: interaction assertions on statics pin implementation detail, so reserve them for negative and cost-related guarantees.

## The handle replaces the mock instance Every Mockito verification needs a subject. For instance mocks it is the mock object: `verify(repo).save(entity)`. Static calls have no object, so the `MockedStatic<T>` returned by `mockStatic` is the subject, and the invocation to check is passed as a lambda that performs the call: ```java mocked.verify(() -> Files.readString(path)); ``` Mockito runs that lambda while in verification mode, sees which static method was called with which arguments, and matches it against what the code under test recorded. The mental model is identical to instance verification; only the syntax for naming the invocation differs. ## Modes, matchers and captors The overload `verify(Verification, VerificationMode)` accepts the full set of modes: - `times(n)`, and the default is `times(1)`; - `never()` — useful to assert a dangerous static was avoided; - `atLeast(n)`, `atLeastOnce()`, `atMost(n)`, `atMostOnce()`; - `timeout(ms)` for asynchronously triggered calls — though thread-locality usually rules this out; - `description(...)` to attach a failure message. Argument matchers behave as usual inside the lambda: `mocked.verify(() -> Codec.encode(anyString(), eq(UTF_8)))`. The rule that all arguments must be matchers if any argument is a matcher still holds — mixing a raw value with `any()` throws `InvalidUseOfMatchersException`. `ArgumentCaptor` also works: pass `captor.capture()` inside the lambda and read `captor.getValue()` afterwards. ## verifyNoMoreInteractions and verifyNoInteractions `mocked.verifyNoMoreInteractions()` asserts that every static invocation on that class recorded in the scope has already been verified — the same semantics as the instance version, scoped to this class's statics on this thread. `mocked.verifyNoInteractions()` asserts none happened at all. Both are strong assertions: they tie the test to the exact set of static calls made, so they are best used where the point of the test *is* the interaction set (for example, proving a cache prevented a second call to an expensive static). ## Ordering constraints Verification must run **inside** the scope, before `close()`. After closing, the handle no longer holds a live registration and verification is not meaningful. In practice this means the assert phase lives inside the try-with-resources block, which is slightly awkward but harmless; if you dislike it, capture the values you need into local variables inside the block and assert on them after. Ordered verification across a static mock and instance mocks is not supported the way `InOrder` works for multiple instance mocks; if ordering across a static and an instance matters, the test is usually describing a sequence that would be better expressed with an injected collaborator. ## Thread-locality applies to verification too Only calls made on the registering thread were intercepted, so only those can be verified. A verification that fails with "wanted but not invoked" while you can see the call happening in a log is the classic signature of the production code running that call on another thread. ## Where verification of statics is worth it Static verification is most valuable for negative assertions — proving a legacy static that writes to a file, sends a metric, or exits the JVM was *not* called on a given path — and for counting calls to expensive statics behind a cache. For positive behaviour, asserting on the outcome the code produces is usually a better test than asserting that a specific static was invoked, because the latter locks the test to the implementation.

  • A static verification fails with 'wanted but not invoked' even though logging shows the call happened. What is the most likely cause?
    The call ran on a thread other than the one that opened the scope. Static-mock registration is thread-local, so the call went to the real method and was never recorded. Force the code to run synchronously in the test — for example with a same-thread executor — or restructure so the static call happens on the test thread.
  • Can you mix a literal argument and a matcher inside the verification lambda?
    No. The all-or-nothing matcher rule applies exactly as for instance mocks: if any argument uses a matcher, every argument must, so wrap literals in eq(...). Mixing them throws InvalidUseOfMatchersException, and because the misuse is detected on the next Mockito interaction the error can surface in a confusingly different place.

saying these in an interview costs you the question

  • Trying Mockito.verify(SomeClass.class) instead of verifying through the MockedStatic handle.
  • Verifying after the try-with-resources block has closed the scope.
  • Mixing raw values and matchers inside the verification lambda.
  • Assuming verifyNoMoreInteractions on the handle covers unrelated mocks or other classes.
  • Expecting to verify static calls made on background threads.

context