Injecting Mocks with @Mock and @InjectMocks Annotations

Illustration for Injecting Mocks with @Mock and @InjectMocks Annotations
By Last updated:

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 of EmailService.
  • @InjectMocks automatically injects the mock into UserService.

How Injection Works

Mockito tries different strategies in order:

  1. Constructor Injection (preferred if a suitable constructor is found).
  2. Setter Injection (if setters exist).
  3. 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.