Mockito
Java
Dependency Injection
Software Testing
Autowired Fields

Mockito Inject real objects into private @Autowired fields

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Mockito is a popular mocking framework for unit tests in Java. However, there’s often confusion around how to use it to mock or inject real objects into private @Autowired fields in Spring-based applications. This article aims to demystify this process and provide clear examples and explanations.

Understanding the Problem

In Spring Framework, dependency injection allows the framework to control the creation of beans and their injection into components where they are needed. @Autowired is used to automatically inject beans by type. However, during unit testing, you might encounter a scenario where you need to inject a real object or a mocked instance into a private field annotated with @Autowired in order to isolate the test scenario.

Use Case for Mockito in Spring Tests

Suppose you have a service class which depends on a repository, and you need to test this service in isolation. Let’s consider an example:

java
1@Service
2public class UserService {
3    @Autowired
4    private UserRepository userRepository;
5
6    public User getUserById(String id) {
7        return userRepository.findById(id).orElse(null);
8    }
9}

Here, UserService has a private UserRepository field. During unit testing, you don’t want to use the actual repository as it would make the test an integration test and potentially slow and brittle.

Injecting Mocks into Private Fields

Mockito alone does not support injecting into private fields directly; this is where reflection comes in handy. By using reflection, we can bypass the access control checks and alter the private fields.

Using ReflectionTestUtils

Spring offers ReflectionTestUtils which is specifically designed for such use cases in tests. It provides methods to set private fields without needing direct access to them. Here’s how you can use it in a test:

java
1@RunWith(SpringRunner.class)
2@SpringBootTest
3public class UserServiceTest {
4
5    @Autowired
6    private UserService userService;
7
8    @MockBean
9    private UserRepository mockRepository;
10
11    @Before
12    public void setUp() {
13        User dummyUser = new User("1", "John Doe");
14        Mockito.when(mockRepository.findById("1")).thenReturn(Optional.of(dummyUser));
15    }
16
17    @Test
18    public void testGetUserById() {
19        User result = userService.getUserById("1");
20        assertNotNull(result);
21        assertEquals("John Doe", result.getName());
22    }
23}

In this setup, @MockBean creates a mock instance of UserRepository and replaces the bean in the application context, which is then injected into UserService.

When to Use Real Instances

Sometimes, you want to test the interaction between real components instead of mock interactions. For this, you can use @SpyBean to wrap a real bean with spying behavior.

java
@SpyBean
private UserRepository realRepository;

Table summarizing key annotation interactions:

AnnotationUsage
@AutowiredAutomatically inject beans by type.
@MockBeanCreates a mock instance and adds/replaces the bean in the Spring application context.
@SpyBeanWraps the real bean in a spy to monitor real method calls while retaining the original behavior.
@InjectMocksUsed by Mockito to inject mocked fields into the tested object automatically. Not compatible with @Autowired.

Advanced Scenario: Hybrid Approach using @SpringBootTest

There might be cases where you need a more integrated environment while still controlling certain bean definitions, such as when the application context setup is complex. In such cases, using @SpringBootTest along with mock definitions can be beneficial.

Here's a small illustration:

java
1@RunWith(SpringRunner.class)
2@SpringBootTest
3public class UserServiceIntegrationTest {
4
5    @Autowired
6    private UserService userService;
7
8    @MockBean
9    private UserRepository userRepository;
10
11    // test methods similar to above
12}

Conclusion

In conclusion, Mockito with Spring can be powerful, but one must understand when to use mocks, spies, or real beans. Utilizing tools like ReflectionTestUtils or annotations like @MockBean and @SpyBean, one can effectively write unit tests or slightly broader scoped integration tests, managing dependencies cleanly and ensuring tests remain focused on their target behavior.


Course illustration
Course illustration

All Rights Reserved.