
本文详解在 Mockito 测试中如何将 @InjectMocks 创建的被测对象(如 Player)自动注入到其依赖对象(如 Game)中,并对比 Spring、Spring Boot 及纯 Java 场景下的不同实现方式。
本文详解在 mockito 测试中如何将 `@injectmocks` 创建的被测对象(如 `player`)自动注入到其依赖对象(如 `game`)中,并对比 spring、spring boot 及纯 java 场景下的不同实现方式。
在单元测试中,@InjectMocks 并非用于“提供可被注入的实例”,而是用于创建被测类的实例,并自动将标注了 @Mock 或 @Spy 的字段注入其构造函数或 setter 中。因此,若你希望 Game 类使用一个已由 @InjectMocks 构建的 Player 实例,需明确:@InjectMocks 不会将自身“暴露为可注入的 Bean”——它只负责单次构建与注入,不参与后续依赖链传递。
✅ 正确做法:按测试目标选择注入策略
1. 使用 @Mock + @InjectMocks 进行纯 Mockito 单元测试(推荐)
当 Player 是被测对象的依赖项(而非被测对象本身),应将其声明为 @Mock,再让 @InjectMocks Game 自动完成注入:
@ExtendWith(MockitoExtension.class)
class GameTest {
@Mock
private Player player; // 模拟依赖,非被测对象
@InjectMocks
private Game game; // 被测对象,自动注入 player
@Test
void shouldReturnPlayerName() {
// 给 mock 行为打桩
when(player.getName()).thenReturn("Alice");
String result = game.getPlayerName();
assertEquals("Alice", result);
verify(player).getName();
}
}
⚠️ 注意:此时
Player是模拟对象(@Mock),不是通过@InjectMocks Player player创建的真实/半真实实例。若你确实需要Player是部分真实对象(如仅模拟其Tool依赖),请改用@Spy配合@Mock Tool。
2. 使用 Spring 容器管理真实对象(集成测试场景)
若需 Player 和 Game 均为真实实例(例如验证完整构造链),应交由 Spring 容器管理依赖:
@SpringBootTest // 或 @ContextConfiguration(非 Boot 项目)
class GameIntegrationTest {
@Autowired
private Game game; // Spring 自动装配完整对象图:Game ← Player ← Tool
@Test
void shouldLoadGameWithRealPlayer() {
assertNotNull(game);
assertNotNull(game.getPlayer()); // Player 已由 Spring 实例化并注入
}
}
此时无需手动 new Game(player),Spring 会解析 @Inject 构造器并递归注入 Tool → Player → Game。
3. 手动组装(无框架轻量测试)
在不引入任何 DI 框架时,必须显式构造依赖链:
class GameManualTest {
private Game game;
@BeforeEach
void setUp() {
Tool tool = new Tool();
Player player = new Player(tool); // 真实 Player 实例
game = new Game(player); // 手动注入
}
@Test
void shouldWorkWithRealObjects() {
assertEquals("expected", game.play());
}
}
❌ 常见误区澄清
-
@InjectMocks Player player;仅创建并注入Player实例到当前测试类字段,不会使其成为Game构造器的自动候选; -
@InjectMocks Game game;不会自动识别并复用同名的player字段——它只扫描@Mock/@Spy字段匹配类型,而@InjectMocks Player player是一个 被注入对象,不是 可注入的 mock; -
MockitoAnnotations.initMocks(this)(旧版 API)已被@ExtendWith(MockitoExtension.class)取代,务必使用扩展模型以确保生命周期正确。
✅ 总结:三步决策法
-
目标是隔离测试
Game行为? →@Mock Player+@InjectMocks Game(最常用) -
目标是验证整个 DI 链路(含
Tool→Player→Game)? → 用@SpringBootTest或@ContextConfiguration -
无框架且需最小依赖? →
@BeforeEach中手动new并组装
选择合适策略,才能让测试既精准又可维护。










