How do you correctly match null and nullable arguments in Mockito, and how does that interact with typed any(Class) and primitive matchers?
answer
- isNull() = only null
- nullable(T) = null OR a T
- any(T.class) and anyString() EXCLUDE null
- bare any() includes null
- primitives (anyInt) never null
basics
~20 sTo 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 sNull 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// 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
Knows isNull() matches null and that any() can match null too.
Distinguishes isNull()/nullable()/any(Class)/anyString() with respect to null and picks the right one for the path under test.
Explains why modern Mockito excludes null from typed/primitive matchers and diagnoses the silent-default bug when null isn't matched.
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.