@Mock and @InjectMocks with MockitoExtension
Creating mocks with mock(...) and passing them to the constructor by hand is fine for two dependencies. With more, the setup grows. Mockito can do it for you: fields marked @Mock become mocks, and the field marked @InjectMocks gets an object of the class under test with those mocks passed in. The JUnit extension MockitoExtension connects this to the test lifecycle.
The annotations
// src/test/java/com/example/blog/PostServiceAnnotationsTest.java
package com.example.blog;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class PostServiceAnnotationsTest {
@Mock
PostRepository repository;
@Mock
Notifier notifier;
@InjectMocks
PostService service;
@Test
void publish() {
when(repository.findById(1L))
.thenReturn(Optional.of(new Post(1L, "Hello", "anna", false)));
Post post = service.publish(1L);
assertTrue(post.published());
verify(notifier).send("anna", "Your post \"Hello\" is published");
}
}| Part | What it does |
|---|---|
@ExtendWith(MockitoExtension.class) | Before each test, creates the mocks and fills the fields |
@Mock | The field becomes a new mock of its type |
@InjectMocks | Creates PostService and passes the mocks to the constructor by type |
@Captor | The field becomes an ArgumentCaptor: @Captor ArgumentCaptor<Post> captor; |
@InjectMocks picks the constructor with the most parameters. If there is no suitable constructor, it tries setters and fields. Constructor injection is the most reliable: the service cannot exist without its dependencies.
Strict stubbing
MockitoExtension is strict: it fails a test that stubs something the code never uses. Such a when is usually a leftover from copying or a sign that the test checks something other than it thinks:
// src/test/java/com/example/blog/PostServiceAnnotationsTest.java
@Test
void publishedIsEmpty() {
when(repository.findById(1L))
.thenReturn(Optional.of(new Post(1L, "Hello", "anna", false)));
when(repository.findAll()).thenReturn(List.of());
assertEquals(0, service.published().size());
}org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at com.example.blog.UnnecessaryTest.publishedIsEmpty(UnnecessaryTest.java:29)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.published() calls only findAll(), so the stub of findById is unnecessary — delete it. In rare cases where a stub is shared by tests and not every test uses it, mark it lenient().when(...).
Common mistake
The annotations alone do nothing: without @ExtendWith(MockitoExtension.class) nobody creates the mocks, and the fields stay null:
[ERROR] com.example.blog.NoExtensionTest.publish -- Time elapsed: 0.050 s <<< ERROR!
java.lang.NullPointerException: Cannot invoke "com.example.blog.PostRepository.findById(long)" because "this.repository" is null
at com.example.blog.NoExtensionTest.publish(NoExtensionTest.java:24)A NullPointerException on a @Mock field almost always means a forgotten @ExtendWith. Add it to the class.