skip to content

A test class declares fields annotated with Mockito's @Mock, and the test fails with NullPointerException the first time one of those fields is used. Why are they null, and what are the ways to make them non-null?

level: juniorimportance: must knowfreq 62%

answer

  1. annotation is inert metadata
  2. extension / openMocks / runner-or-rule — pick one
  3. @RunWith silently ignored by JUnit 5
  4. mixed JUnit 4 + 5 imports = setup never runs
  5. mockito-junit-jupiter artifact needed for the extension

basics

~20 s

@Mock is only metadata; something must scan the test instance and assign the mocks. Enable one initializer: @ExtendWith(MockitoExtension.class) on JUnit 5, MockitoAnnotations.openMocks(this) in a setup method, or the JUnit 4 MockitoJUnitRunner/MockitoRule. Without one, the fields stay null.

solid answer

~40 s

Annotations do nothing on their own. Some component has to reflect over the test instance's fields, create a mock for each `@Mock` (and `@Spy`, `@Captor`, then populate `@InjectMocks`) and assign them. Mockito ships three such initializers: - **JUnit 5:** `@ExtendWith(MockitoExtension.class)` on the class (from `mockito-junit-jupiter`). - **Framework-agnostic:** `MockitoAnnotations.openMocks(this)` called in `@BeforeEach`/`@Before`/`setUp`; it returns an `AutoCloseable` you should close afterwards. - **JUnit 4:** `@RunWith(MockitoJUnitRunner.class)` or a `@Rule public MockitoRule rule = MockitoJUnit.rule();`. The usual causes of the failure are: no initializer at all; a JUnit 4 `@RunWith` left on a class now executed by JUnit 5, which ignores it entirely; mixed `org.junit.Test` and `org.junit.jupiter.api.Test` imports so the setup method never runs; or the initializer being invoked after the fields are used.

code

java · 15 lines
java
@ExtendWith(MockitoExtension.class)
class WithExtensionTest {
    @Mock OrderRepository repository;      // non-null before each test
}

class WithOpenMocksTest {
    @Mock OrderRepository repository;
    private AutoCloseable mocks;

    @BeforeEach
    void init() { mocks = MockitoAnnotations.openMocks(this); }

    @AfterEach
    void release() throws Exception { mocks.close(); }
}

go deeper

for a junior

Recall that an initializer is required and name at least the JUnit 5 extension and openMocks(this).

for a middle

Name all three mechanisms, know which JUnit generation each belongs to, and explain the silently-ignored @RunWith trap.

for a senior

Add the diagnosis checklist — one initializer, engine match, consistent imports, artifact present — and why stacking initializers loses stubbing.

for a principal

Set the team convention: one initializer declared in a shared base class or template, consistent JUnit generation across the module, so this class of failure cannot recur.

## Why the field is null `@Mock` is a plain annotation retained at runtime. Declaring it changes nothing about the field: at construction time the JVM initializes it to `null` like any uninitialized reference. Something must *process* the annotation — walk the test instance's declared fields (including inherited ones), see the annotation, call `Mockito.mock(...)` with the field's type and name, and assign the result by reflection. Mockito ships three processors, and you pick exactly one. ## Option 1 — the JUnit 5 extension ```java @ExtendWith(MockitoExtension.class) class CheckoutServiceTest { @Mock OrderRepository repository; } ``` It lives in the separate `mockito-junit-jupiter` artifact (test scope), so a missing dependency shows up as an unresolvable `MockitoExtension`. The extension initializes annotated fields before each test method, tears down afterwards, applies strict stubs by default, and can also resolve `@Mock`-annotated **parameters** of test and lifecycle methods — handy for a mock only one test needs. ## Option 2 — openMocks, called explicitly ```java class CheckoutServiceTest { @Mock OrderRepository repository; private AutoCloseable mocks; @BeforeEach void init() { mocks = MockitoAnnotations.openMocks(this); } @AfterEach void tearDown() throws Exception { mocks.close(); } } ``` This is framework-agnostic — JUnit 4, JUnit 5, TestNG, or a plain `main`. It is the escape hatch when you cannot register the extension, for example inside a test infrastructure base class shared across frameworks. `openMocks` replaced the older `initMocks(this)`, which is deprecated because it did not give you the closeable handle. ## Option 3 — the JUnit 4 runner or rule `@RunWith(MockitoJUnitRunner.class)` initializes annotations and validates Mockito usage after each test. `@Rule public MockitoRule rule = MockitoJUnit.rule();` does the same while leaving the runner slot free for another runner (a parameterized runner, a Spring runner). Both are JUnit 4 only. ## The failure modes that produce null fields 1. **No initializer at all.** The most common by far, especially when a test class is created by copying one that had the extension and dropping the annotation. 2. **A JUnit 4 runner on a JUnit 5 test.** JUnit Jupiter does not understand `@RunWith`; it neither honours it nor complains, so the class runs with uninitialized mocks. The same happens with a JUnit 4 `@Rule` field under Jupiter. 3. **Mixed imports.** `@BeforeEach` from Jupiter combined with `org.junit.Test` (JUnit 4), or vice versa: the setup method belongs to a lifecycle the executing engine ignores, so `openMocks` never runs. Symptom: mocks are null even though the setup method visibly exists. 4. **The initializer runs too late or not for this instance.** Calling `openMocks(this)` from a helper that receives a different object, or initializing in `@BeforeAll` (static, wrong instance), leaves the per-test fields null. 5. **The annotation is not Mockito's.** Importing a different framework's `@Mock` — or Spring's bean-replacing mock annotation, which requires a Spring test context rather than the Mockito extension — is a quiet copy-paste trap. 6. **The field is static, or the class is executed by an engine that constructs test instances differently.** Mockito's processors assign instance fields on the test instance they are handed. ## Diagnosing quickly Ask in order: Is there exactly one initializer? Does the executing engine match it (Jupiter extension vs JUnit 4 runner/rule)? Are all lifecycle and test annotations from the same JUnit generation? Is `mockito-junit-jupiter` on the test classpath? Is the setup method non-static and non-private? That checklist covers essentially every occurrence. ## Do not stack initializers Registering the extension *and* calling `openMocks` in `@BeforeEach` is redundant: fields are created twice, the second set replacing the first, and any stubbing done between the two is lost. It also confuses session validation. Pick one mechanism per class and let base classes declare it rather than each subclass repeating it. ## A note on the alternative You can always avoid the question entirely by creating collaborators with `Mockito.mock(...)` and wiring the class under test through its constructor. That needs no initializer at all, and some teams prefer it for exactly that reason. The annotation route buys concision and mock naming; the manual route buys explicitness. ## Interview framing Say it in one sentence — the annotation is inert until an initializer processes it — then name the three initializers and the two classic traps (JUnit 4 runner under Jupiter, mixed imports). That is the complete answer.

  • A test class carries @RunWith(MockitoJUnitRunner.class) but the mocks are still null. What is going on?
    The class is almost certainly being executed by JUnit 5, which does not understand `@RunWith` and ignores it without warning, so nothing ever initializes the annotated fields. Check whether the test methods import `org.junit.jupiter.api.Test`. The fix is to replace the runner with `@ExtendWith(MockitoExtension.class)` and make all lifecycle annotations Jupiter ones.
  • Is it harmful to both register the Mockito JUnit 5 extension and call openMocks(this) in @BeforeEach?
    Yes, though not always visibly. The fields are populated twice and the second set overwrites the first, so any stubbing performed between the two initializations is silently discarded, and you now have two overlapping mocking sessions to validate. Choose one mechanism per class; put it in the base class if several classes need it.

saying these in an interview costs you the question

  • Believing the @Mock annotation alone creates the mock
  • Leaving a JUnit 4 @RunWith or @Rule on a JUnit 5 test class and expecting it to work
  • Mixing org.junit.Test with @BeforeEach and wondering why setup never ran
  • Calling openMocks in a static @BeforeAll and expecting instance fields to be set
  • Registering both the extension and openMocks 'to be safe'

context