
在 Mockito 中,若需对被测类自身的方法进行模拟(而非仅依赖注入的协作对象),应使用 @Spy 创建部分模拟对象,而非 @InjectMocks;后者仅用于装配真实实例与 mock 依赖,无法拦截类内方法调用。
在 mockito 中,若需对被测类自身的方法进行模拟(而非仅依赖注入的协作对象),应使用 `@spy` 创建部分模拟对象,而非 `@injectmocks`;后者仅用于装配真实实例与 mock 依赖,无法拦截类内方法调用。
在编写单元测试时,常遇到一种典型场景:待测类(如 Test)内部包含多个方法,其中 mainMethod() 是入口逻辑,而它又调用了本类的另一个方法 methodtWhichNeedMock()。此时,我们希望 隔离测试 mainMethod() 的行为,避免其实际执行 methodtWhichNeedMock() 的业务逻辑(例如远程调用、IO 或复杂计算),而是由测试主动控制该方法的返回值。
但若错误地使用 @InjectMocks(如原代码所示),Mockito 实际创建的是一个真实对象实例,所有未显式 mock 的方法仍会执行原始逻辑 —— 这正是你观察到“methodtWhichNeedMock 总是运行真实代码”的根本原因。@InjectMocks 仅负责将 @Mock 标记的依赖自动注入目标对象,它本身不提供对被测类方法的拦截能力。
✅ 正确做法是:使用 @Spy 注解创建部分模拟(Partial Mock)对象。@Spy 会基于真实对象创建代理,既保留默认行为(调用真实方法),又允许你通过 when(...).thenReturn(...) 或 doReturn(...).when(...) 显式 stub 某些方法:
@ExtendWith(MockitoExtension.class)
class TestClass {
@Spy
Test service; // ← 关键:使用 @Spy 而非 @InjectMocks
@Test
void mainMethod() {
// 注意:参数需与实际调用一致(原测试中传的是 "s1"/"s2",但 mainMethod 内调用的是 "Hello"/"World")
when(service.methodtWhichNeedMock("Hello", "World")).thenReturn("mocked result");
String result = service.mainMethod();
assertEquals("mocked result", result);
}
}
⚠️ 重要注意事项:
-
参数匹配必须精确:
when(...)中的参数需与mainMethod()内部实际调用的参数完全一致(如示例中应为"Hello"和"World",而非"s1"/"s2"),否则 mock 不生效,仍执行真实方法。 -
避免
@InjectMocks+@Spy混用:二者语义冲突;@Spy对象应独立声明,无需注入。 -
慎用
doReturn().when()链式调用:对于void方法或需要处理异常的场景更适用,但对普通返回值方法,when().thenReturn()更直观安全。 -
@Spy的局限性:私有方法、静态方法、final 方法无法被 spy 拦截(需结合mockito-inline或重构设计)。
总结:当测试目标聚焦于同一类中方法间的协作逻辑,且需控制内部方法行为时,@Spy 是标准且可靠的解决方案。它让测试具备精准的“切片”能力——只 mock 需要隔离的部分,其余保持真实,兼顾可测性与真实性。










