Uninitialized Mockito Annotation
ID |
java.annotated_mockito_objects_init |
Severity |
critical |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
mockito, reliability, testing |
Description
Reports a class whose @Mock, @Spy, @Captor or @InjectMocks fields are never initialized by any of Mockito’s supported triggers.
Rationale
Unlike Mockito.mock(…) or Mockito.spy(…), the annotations do nothing by themselves. Without a trigger, every annotated field stays null: a call on it throws NullPointerException, and a test that happens not to touch the field passes while verifying nothing real.
class BillingServiceTest {
@Mock private PaymentGateway gateway;
@InjectMocks private BillingService serviceUnderTest;
@Test
void chargesTheGateway() {
serviceUnderTest.charge(100);
}
}
Remediation
Pick one of Mockito’s initialization triggers: a JUnit 4 runner, a JUnit 5 extension, a @Rule, or an explicit MockitoAnnotations.openMocks(this) call from a setup method, a constructor or an instance initializer block.
@ExtendWith(MockitoExtension.class)
class BillingServiceTest {
@Mock private PaymentGateway gateway;
@InjectMocks private BillingService serviceUnderTest;
@Test
void chargesTheGateway() {
serviceUnderTest.charge(100);
}
}
What is not reported
Only the first annotated field is reported — the rest share the same missing trigger, and only fields left for the annotation to fill in count: a field assigned at its declaration (@Spy Foo foo = spy(new Foo()), @InjectMocks Service s = new Service()) is a real object whatever the annotation does, and is never reported.
MockitoAnnotations.openMocks(this) / initMocks(this) counts wherever a class can legitimately run it before its tests: a @Before/@BeforeEach method, a constructor, or an instance initializer block. The constructor form is how a private per-test "context" holder initializes its own mocks.
A @RunWith(…) or @ExtendWith(…) is accepted regardless of the argument, since verifying what a third-party runner or extension actually does at test time is out of scope. A @Nested class inherits its enclosing class’s trigger. A subclass inherits its superclass’s trigger: a superclass in the same file is checked in full, and a superclass in another file is resolved through the project-wide software model, which answers for its class-level annotations. When the extends clause names a type the model does not know at all, the class is left unreported rather than guessed at.
A trigger reached only through a custom, meta-annotated annotation, or through a @Rule field or an openMocks call declared on a superclass in another file (the model holds no method bodies), is not detected.