When writing unit tests, we often need to mock dependencies and inject them into the class under test. While you can do this manually, Mockito provides powerful annotations like @Mock
and @InjectMocks
to simplify the process.
In this tutorial, we’ll explore how to use @Mock
and @InjectMocks
in Mockito, their differences, setup, and best practices.
Why Use @Mock and @InjectMocks?
- Simplify Test Setup: Avoid repetitive manual mocking and constructor injection.
- Readable Tests: Annotations make dependencies and injection explicit.
- Maintainable Code: Reduces boilerplate setup in large test classes.
- Improved CI/CD Reliability: Consistent initialization across test runs.
Think of @Mock
as casting actors for a play, and @InjectMocks
as handing them scripts inside the main scene.
Example: User Service with Dependency
Class Under Test
class EmailService {
void send(String email, String message) {
System.out.println("Sending email to " + email);
}
}
class UserService {
private final EmailService emailService;
UserService(EmailService emailService) {
this.emailService = emailService;
}
void registerUser(String email) {
// Registration logic...
emailService.send(email, "Welcome!");
}
}
Using @Mock and @InjectMocks
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 static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private EmailService emailService; // Creates a mock
@InjectMocks
private UserService userService; // Injects mock into UserService
@Test
void shouldSendWelcomeEmailOnRegistration() {
userService.registerUser("alice@example.com");
verify(emailService).send("alice@example.com", "Welcome!");
}
}
Here:
@Mock
creates a mock ofEmailService
.@InjectMocks
automatically injects the mock intoUserService
.
How Injection Works
Mockito tries different strategies in order:
- Constructor Injection (preferred if a suitable constructor is found).
- Setter Injection (if setters exist).
- Field Injection (as a last resort).
Manual Mocking vs @InjectMocks
Without @InjectMocks
@Test
void manualInjection() {
EmailService mockEmail = mock(EmailService.class);
UserService userService = new UserService(mockEmail);
userService.registerUser("bob@example.com");
verify(mockEmail).send("bob@example.com", "Welcome!");
}
With @InjectMocks
@InjectMocks
private UserService userService;
Much cleaner and reduces boilerplate.
Combining with @BeforeEach
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@BeforeEach
void init() {
// Additional setup if needed
}
}
This ensures each test starts with fresh mocks.
Best Practices
- Use
@InjectMocks
for classes with dependencies. - Prefer constructor injection in production code — works best with Mockito.
- Avoid overusing field injection — can lead to fragile tests.
- Keep tests focused — inject only necessary mocks.
- Combine
verify()
with stubbing (when()
) for complete coverage.
Mockito + Testcontainers Example
While @Mock
and @InjectMocks
simplify unit tests, Testcontainers help with integration tests using real dependencies.
import org.testcontainers.containers.PostgreSQLContainer;
@Test
void shouldStartDatabase() {
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
postgres.start();
assert postgres.isRunning();
}
}
Together, they provide a strong hybrid testing strategy.
Version Tracker
- JUnit 4 → JUnit 5: Mockito switched from
@RunWith(MockitoJUnitRunner.class)
to@ExtendWith(MockitoExtension.class)
. - Mockito Updates: Improved injection strategies and reduced boilerplate.
- Testcontainers Ecosystem: Growing support for real dependency validation.
Conclusion & Key Takeaways
@Mock
and @InjectMocks
make dependency injection in tests easier, cleaner, and more maintainable. They are essential for scalable unit testing in Java.
Key Takeaways:
@Mock
creates mock objects.@InjectMocks
injects mocks automatically.- Prefer constructor injection for better testability.
- Use annotations to reduce boilerplate.
- Combine with Testcontainers for full coverage.
FAQ
1. What’s the difference between @Mock and @InjectMocks?@Mock
creates mock objects, while @InjectMocks
injects them into the class under test.
2. Do I always need @InjectMocks?
No, you can inject manually, but @InjectMocks
reduces boilerplate.
3. Which injection strategy does Mockito prefer?
Constructor injection, then setters, then fields.
4. Can I use multiple @InjectMocks in one test?
Yes, but each should represent a different class under test.
5. Does @InjectMocks work with final fields?
No, final fields cannot be injected.
6. Should I combine @Mock with @Spy?
Only when partial mocking is required.
7. Do mocks persist across tests?
No, fresh mocks are created for each test run.
8. What’s the difference between JUnit 4 and 5 setup?
JUnit 5 uses @ExtendWith(MockitoExtension.class)
instead of runners.
9. Can I mock static methods with @Mock?
No, use mockStatic()
for static methods.
10. How does this work in CI/CD pipelines?
Annotations ensure consistent, repeatable test setups across builds.