Mockito: Inject real objects into private @Autowired fields
Mockito: Inject real objects into private @Autowired fields
技术背景
在使用 Mockito 进行单元测试时,通常会使用 @Mock
和 @InjectMocks
注解来注入依赖到使用 Spring 的 @Autowired
注解标注的私有字段。但有时,我们可能需要将真实对象注入到这些私有 @Autowired
字段中,而不只是注入模拟对象。这在处理遗留代码时尤为有用,因为设置模拟对象可能需要大量的 when(...).thenReturn(...)
语句来避免空指针异常等问题。
实现步骤
方法一:使用 @Spy 注解
- 创建一个测试类,并使用
@RunWith(MockitoJUnitRunner.class)
注解。 - 使用
@Spy
注解标注一个真实对象的实例。 - 使用
@InjectMocks
注解标注要注入依赖的类的实例。
示例代码如下:
1 |
|
在上述代码中,RealServiceImpl
实例会被注入到 demo
对象中。
方法二:使用 Spring 的 ApplicationContext
- 创建一个测试类,并使用
@RunWith(MockitoJUnitRunner.class)
注解。 - 使用
@Inject
注解注入ApplicationContext
。 - 使用
@Spy
注解标注要注入的服务。 - 在
@Before
方法中,从ApplicationContext
获取真实对象实例并赋值给@Spy
注解标注的字段。
示例代码如下:
1 |
|
方法三:使用 ReflectionTestUtils
- 在测试类中使用
@Spy
、@Mock
和@InjectMock
注解。 - 在
@BeforeEach
方法中,使用ReflectionTestUtils.setField
方法将真实对象注入到私有字段中。
示例代码如下:
1 |
|
方法四:使用构造函数注入
- 在测试类中声明一个模拟对象和要测试的类的实例。
- 在
@BeforeEach
方法中,通过构造函数将模拟对象或真实对象传递给要测试的类的实例。
示例代码如下:
1 |
|
方法五:使用 JUnit5/Mockito 扩展
如果需要注入 String
等对象,可以使用 JUnit5/Mockito 扩展,例如 mockito-object-injection。
示例代码如下:
1 |
|
最佳实践
- 使用构造函数注入:在设计类时,尽量使用构造函数注入依赖,这样可以使代码更易于测试,避免使用反射等复杂的注入方式。
- 谨慎使用 @Spy:
@Spy
注解会创建一个真实对象的部分模拟,可能会使测试变得复杂和脆弱。只有在处理遗留代码或无法轻易更改的代码时才使用。 - 遵循单一职责原则:确保每个类的职责单一,这样可以减少依赖注入的复杂性,使测试更加简单和可靠。
常见问题
- Mockito 无法模拟 final 类、匿名类和基本类型:如果需要注入
String
等 final 类,可以使用其他方法,如 JUnit5/Mockito 扩展。 - NullPointerException:在使用
@Before
或@BeforeEach
方法时,确保对象已经正确初始化。如果使用 Spring 的ApplicationContext
,确保 Spring 上下文已经正确加载。 - 部分模拟的问题:使用
@Spy
注解可能会导致部分模拟的问题,使测试变得复杂。在使用@Spy
之前,仔细阅读spy()
的 Javadoc,了解其潜在的问题。
Mockito: Inject real objects into private @Autowired fields
https://119291.xyz/posts/2025-04-29.mockito-inject-real-objects-into-private-autowired-fields/