
本文详解 Mockito spy() 的核心机制与常见误区,重点说明为何直接使用 when().thenReturn() 会导致真实方法被调用,并提供安全、可靠的替代方案(doReturn().when()),确保仅指定方法被 stub,其余方法保持真实行为。
本文详解 mockito `spy()` 的核心机制与常见误区,重点说明为何直接使用 `when().thenreturn()` 会导致真实方法被调用,并提供安全、可靠的替代方案(`doreturn().when()`),确保仅指定方法被 stub,其余方法保持真实行为。
在使用 Mockito 进行部分模拟(partial mocking)时,spy() 是一个强大但易被误用的工具。其设计初衷是:对被 spy 的对象,所有方法默认执行真实逻辑;仅当显式 stub 时,才覆盖对应行为。然而,许多开发者会错误地沿用 mock 对象的写法——即用 when(obj.method()).thenReturn(...) 来 stub spy 对象的方法,这恰恰触发了 spy 的“陷阱”:when(...).thenReturn(...) 在执行时会先真实调用该方法一次,导致副作用(如数据库操作、日志打印、状态变更)意外发生。
正如问题中所示,serviceMethod_1() 在测试 setup 阶段就被真实执行了一次(控制台输出 serviceMethod_1: result = call_serviceMethod_1_REAL),根本原因正是:
when(myServiceSpy.serviceMethod_1()) // ⚠️ 此处已触发真实调用!
.thenReturn("call_serviceMethod_1_MOCK");✅ 正确做法是使用 doReturn() 系列 API(如 doReturn(), doThrow(), doAnswer()),它们是专为 spy 设计的“前置 stub”语法,完全绕过真实方法调用,直接定义返回行为:
@Before
public void setup() {
// ✅ 安全:不会触发 serviceMethod_1() 的真实逻辑
doReturn("call_serviceMethod_1_MOCK")
.when(myServiceSpy).serviceMethod_1();
// ✅ 同理,若需 stub void 方法,必须用 doNothing() / doThrow()
// doNothing().when(myServiceSpy).voidMethod();
}完整修正后的测试示例:
@Test
public void myService_method_1_spy() {
// 使用 doReturn 替代 when...thenReturn
doReturn("call_serviceMethod_1_MOCK")
.when(myServiceSpy).serviceMethod_1();
List<String> actualList = myServiceSpy.performService();
List<String> expectedList = Arrays.asList(
"call_serviceMethod_1_MOCK",
"call_serviceMethod_2_REAL",
"call_serviceMethod_3_REAL"
);
assertEquals(expectedList, actualList);
verify(myServiceSpy, times(1)).serviceMethod_1(); // 验证 stub 被调用,非真实执行
}⚠️ 关键注意事项:
- doReturn() 必须配合 when(obj).method() 的链式调用(注意括号位置:.when(spy).method(),而非 .when(spy.method()));
- 对于 void 方法,只能使用 doNothing()、doThrow() 或 doAnswer(),when(...).thenXXX() 在 spy 场景下不可用且会抛出异常;
- spy() 对象本身仍是真实实例,构造函数和字段初始化均会执行,因此需确保被 spy 类无副作用构造逻辑;
- 若需更彻底的隔离(如避免构造函数副作用),可考虑 @Mock(answer = Answers.CALLS_REAL_METHODS) + @InjectMocks 组合,或改用 Mockito.mock() + Mockito.CALLS_REAL_METHODS。
总结:spy() 的本质是“真实对象 + 可选覆盖”,而 doReturn().when().method() 是唯一安全、符合直觉的 stub 方式。牢记这一原则,即可精准控制部分模拟行为,避免测试污染与不可预测的副作用。

















