
本文详解在 Mockito 和 Spring 等不同测试环境下,如何将 @Mock/@InjectMocks 创建的模拟对象或真实实例自动注入到被测类(如 Game)中,避免手动构造,提升测试可维护性与可读性。
本文详解在 mockito 和 spring 等不同测试环境下,如何将 `@mock`/`@injectmocks` 创建的模拟对象或真实实例自动注入到被测类(如 `game`)中,避免手动构造,提升测试可维护性与可读性。
在单元测试中,当被测类(如 Game)依赖另一个类(如 Player),而 Player 本身又依赖 Tool 时,我们常需灵活控制其依赖行为:有时需模拟行为(如返回固定值、抛异常),有时需使用真实对象(如集成测试或轻量级容器验证)。关键在于理解 @InjectMocks 的作用边界——它仅负责将 @Mock 或 @Spy 标记的字段(或 @Autowired 兼容的 Bean)注入到被 @InjectMocks 标注的目标实例中,但不会递归解析依赖链中的“中间层”对象(如 Player)是否也应被 mock 或实例化。
✅ 正确使用 @InjectMocks(Mockito 场景)
若目标是测试 Game 的逻辑,且希望 Player 行为可控(例如 getPlayer() 返回 "player1"),应直接 @Mock Player,再通过 @InjectMocks Game 让 Mockito 自动注入该 mock 实例:
@ExtendWith(MockitoExtension.class) // 替代旧版 @RunWith(MockitoJUnitRunner.class)
class GameTest {
@Mock
private Player player; // 直接 mock Player,而非试图 mock Player 内部的 Tool
@InjectMocks
private Game game; // Mockito 自动调用 Game(Player) 构造器,传入 mock player
@Test
void getPlayer() {
when(player.getPlayer()).thenReturn("player1");
String result = game.getPlayer();
assertEquals("player1", result);
}
}
⚠️ 注意:
- 不要对
Player同时使用@Mock和@InjectMocks——@InjectMocks用于被测对象(Game),@Mock用于其依赖项(Player); -
@InjectMocks不会自动创建Player的真实实例并注入其依赖(如Tool),因此无需也不应声明@Mock Tool+@InjectMocks Player; -
@ExtendWith(MockitoExtension.class)是必需的,它自动完成MockitoAnnotations.initMocks(this),确保注解生效。
? 使用真实对象(非 Mock 场景)
若需测试 Game 与真实 Player(及其真实 Tool)的协作,@InjectMocks 不适用——它不创建真实对象。此时需根据技术栈选择方案:
-
纯 Java(无框架):手动构建对象图,清晰可控:
class GameTest { private Game game; @BeforeEach void setUp() { Tool tool = new Tool(); Player player = new Player(tool); // 真实构造 game = new Game(player); // 真实注入 } @Test void getPlayer() { assertEquals("player1", game.getPlayer()); } } -
Spring(XML/Java Config):启用
@ContextConfiguration加载配置类,让 Spring 容器管理Game及其依赖:@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {Tool.class, Player.class, Game.class}) class GameTest { @Autowired private Game game; // Spring 自动装配完整依赖链 @Test void getPlayer() { assertEquals("player1", game.getPlayer()); } } -
Spring Boot:最简方式,
@SpringBootTest自动扫描并加载上下文:@SpringBootTest class GameTest { @Autowired private Game game; @Test void getPlayer() { assertEquals("player1", game.getPlayer()); } }
? 总结与最佳实践
| 场景 | 推荐注解 | 关键点 |
|---|---|---|
| 单元测试(隔离依赖) |
@Mock + @InjectMocks
|
@InjectMocks 仅作用于被测类,依赖项必须显式 @Mock/@Spy
|
| 集成测试(真实对象) |
@SpringBootTest / @ContextConfiguration
|
交由 Spring 容器完成全链路依赖注入 |
| 无框架轻量测试 | 手动 new 构造 |
简单、透明、无额外依赖,适合小型项目或快速验证 |
切勿混淆 @Mock(创建模拟对象)与 @InjectMocks(向目标注入模拟/真实依赖)的职责——前者定义“谁被模拟”,后者定义“谁被测试及如何注入”。合理选择测试粒度与技术栈,才能写出既可靠又易维护的测试代码。










