skip to content

How do you correctly match null and nullable arguments in Mockito, and how does that interact with typed any(Class) and primitive matchers?

level: middleimportance: should knowfreq 45%

answer

  1. isNull() = only null
  2. nullable(T) = null OR a T
  3. any(T.class) and anyString() EXCLUDE null
  4. bare any() includes null
  5. primitives (anyInt) never null

basics

~20 s

To match a null argument use isNull(); to match null or a value use nullable(Type.class) or bare any(). any(Foo.class) and anyString()/anyInt() do NOT match null, so don't rely on them when the code might pass null.

solid answer

~40 s

Null handling trips people up because the matchers differ. Bare `any()` matches anything, including null. The **typed** `any(Foo.class)` matches only non-null instances of `Foo`, so it will *not* match a null argument. Primitive matchers like `anyInt()`/`anyLong()`/`anyString()` also never match null (`anyString()` excludes null in modern Mockito). To explicitly require null, use `isNull()`; to accept either null or a value of a type, use `nullable(Foo.class)`. There's also `isNotNull()`. This matters in tests where the production code may legitimately pass null on some path: a stub written with `any(Foo.class)` silently won't fire for the null call, returning Mockito's default (null/0/false) and producing a confusing failure. So pick `isNull()`/`nullable()` deliberately when null is in play, and remember all of these still obey the all-arguments-are-matchers rule, so wrap any sibling literals in `eq()`.

code

java · 8 lines
java
// any(Key.class) does NOT match null -> stub won't fire on load(null)
when(repo.load(any(Key.class))).thenReturn(fallback);   // BUG for null path

// Accept null OR a real Key:
when(repo.load(nullable(Key.class))).thenReturn(fallback);

// Require exactly null, and obey the mixing rule (eq on the literal):
verify(auditor).record(eq("DELETE"), isNull());

go deeper

for a junior

Knows isNull() matches null and that any() can match null too.

for a middle

Distinguishes isNull()/nullable()/any(Class)/anyString() with respect to null and picks the right one for the path under test.

for a senior

Explains why modern Mockito excludes null from typed/primitive matchers and diagnoses the silent-default bug when null isn't matched.

for a principal

Sets conventions favoring explicit isNull()/nullable() over bare any() for readability, and reviews tests where null paths are silently unstubbed.

## Why null is special Mockito's `any...` matchers were tightened over its history. In modern Mockito (2.x+), the **typed** and **primitive** matchers exclude null on purpose, so that a test asserting "any User" doesn't accidentally pass when the code sent `null`. That makes null an explicit, opt-in concern. ## The null-related matchers - **`isNull()`** — matches only a `null` argument. Use when the test specifically expects null was passed. - **`isNotNull()`** — matches any non-null argument (rarely needed, since typed `any(Class)` already excludes null). - **`nullable(Class<T>)`** — matches `null` **or** a non-null instance of the class. This is the "either" matcher. - **bare `any()`** — matches absolutely anything, including null. It's the loosest and is type-erased, so it returns null as its dummy and matches null too. ## What does NOT match null - **`any(Foo.class)`** — non-null `Foo` only. A null argument will **not** match. - **`anyString()`, `anyList()`, `anyMap()`, `anyCollection()`** — non-null only. - **`anyInt()`, `anyLong()`, `anyBoolean()`, `anyDouble()`, …** — primitive types cannot be null, so the question of matching null doesn't even arise; if the parameter is a boxed `Integer` that is null, `anyInt()` will not match it. ## The classic bug ```java // Production code calls repo.load(null) on the not-found path when(repo.load(any(Key.class))).thenReturn(fallback); ``` Because `any(Key.class)` excludes null, the stub does **not** fire for `load(null)`. Mockito returns the unstubbed default (`null`), and the test fails with a confusing NullPointerException far from the cause. The fix: ```java when(repo.load(nullable(Key.class))).thenReturn(fallback); // matches null OR a Key // or, if ONLY null is expected on this path: when(repo.load(isNull())).thenReturn(fallback); ``` ## Verifying a null was passed ```java verify(auditor).record(eq("DELETE"), isNull()); ``` Note the `eq("DELETE")`: once `isNull()` (a matcher) is used, **every** argument must be a matcher, so the literal `"DELETE"` becomes `eq("DELETE")` — the same all-or-nothing rule as every other matcher. ## Choosing | Intent | Matcher | |---|---| | Exactly null | `isNull()` | | Null or a value of type T | `nullable(T.class)` | | Any non-null T | `any(T.class)` | | Literally anything (incl. null) | `any()` | | Any non-null String/collection | `anyString()` / `anyList()` … | | Any primitive value | `anyInt()` etc. | ## Bottom line Null is opt-in. Typed and primitive `any...` matchers exclude null by design; reach for `isNull()` (only null) or `nullable(T.class)` (null or value) whenever the code under test might pass null — and keep obeying the all-arguments-are-matchers rule.

  • A stub written with when(svc.find(anyString())) returns null even though the method was called. What might be wrong?
    The actual call probably passed a null String. anyString() excludes null, so the stub didn't fire and Mockito returned the default. Use nullable(String.class) (null or a value) or isNull() to cover the null path.
  • What is the difference between nullable(Foo.class) and bare any()?
    Both accept null. nullable(Foo.class) additionally restricts non-null arguments to instances of Foo, giving stronger typing, while bare any() accepts literally anything regardless of type.

saying these in an interview costs you the question

  • Believing any(Foo.class) matches null — it doesn't.
  • Using anyString() expecting it to cover a null String.
  • Reaching for bare any() to handle null when nullable(T)/isNull() express intent better.
  • Forgetting eq() on sibling literals once isNull()/nullable() is used.

context