
本文详解 Mockito spy() 的正确用法,重点解决“为何 when().thenReturn() 仍执行真实方法”这一常见误区,并通过 doReturn().when() 等 void-safe 方法实现精准部分 mock。
本文详解 mockito `spy()` 的正确用法,重点解决“为何 `when().thenreturn()` 仍执行真实方法”这一常见误区,并通过 `doreturn().when()` 等 void-safe 方法实现精准部分 mock。
在使用 Mockito 的 spy() 创建部分模拟对象(partial mock)时,一个典型需求是:让大部分方法调用真实实现,仅对特定方法进行 stub(打桩)。但许多开发者会误用 when(obj.method()).thenReturn(...),导致被 stub 的方法仍意外执行了真实逻辑——这正是你遇到的问题根源。
❌ 错误写法:when().thenReturn() 对 spy 失效
// 危险!spy 下 this.serviceMethod_1() 会被真实调用!
when(myServiceSpy.serviceMethod_1()).thenReturn("call_serviceMethod_1_MOCK");原因在于:when(...).thenReturn(...) 是为 mock 对象 设计的语法糖,它内部会先执行括号内的方法调用以获取目标方法引用。对于 spy,这意味着 serviceMethod_1() 会被立即执行一次(产生副作用、修改状态、抛异常等),之后才建立 stub 规则——这完全违背了“不调用真实逻辑”的初衷。
✅ 正确写法:doReturn().when() —— void-safe stubbing
针对 spy(以及 void 方法),必须使用 do-系列 API(如 doReturn, doThrow, doAnswer):
// ✅ 安全!真实方法不会被执行,stub 立即生效
doReturn("call_serviceMethod_1_MOCK").when(myServiceSpy).serviceMethod_1();该语法绕过实际调用,直接在 spy 的拦截层注册 stub 行为,确保 serviceMethod_1() 在后续调用中返回预设值,且零副作用。
完整修正后的测试示例
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
public class MyServiceImplTest {
@Spy
private MyServiceImpl myServiceSpy;
@Before
public void setup() {
// 注意:spy 必须显式初始化(除非使用 @RunWith(MockitoJUnitRunner.class))
myServiceSpy = new MyServiceImpl();
}
@Test
public void myService_method_1_spy() {
// ✅ 正确 stub:避免触发真实 serviceMethod_1()
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 后的调用)
verify(myServiceSpy, times(1)).serviceMethod_2();
verify(myServiceSpy, times(1)).serviceMethod_3();
}
}⚠️ 关键注意事项
- @Spy 字段需手动初始化:JUnit 4 中 @Spy 不自动实例化,必须在 @Before 或构造中显式赋值(如 myServiceSpy = new MyServiceImpl())。JUnit 5 推荐使用 MockitoExtension。
- 避免混合 mock/spy 操作:不要对同一对象既 mock() 又 spy();spy 应基于真实实例。
- 慎用 spy:Mockito 官方建议优先使用真实对象 + 依赖注入 + 纯 mock 协作者,而非 spy。spy 易引发隐藏状态、线程安全等问题。
-
其他 do-系列方法:
- doThrow(new RuntimeException()).when(spy).voidMethod();
- doAnswer(invocation -> "custom result").when(spy).method();
总结
spy() 的核心价值在于“默认走真实逻辑,按需覆盖”,而能否精准控制覆盖行为,取决于是否选用正确的 stub API:
✅ 对 spy 和 void 方法:始终用 doReturn(...).when(spy).method()
❌ 避免对 spy 使用 when(spy.method()).thenReturn(...)
掌握这一区别,就能真正驾驭 partial mocking,写出稳定、可预测、无副作用的单元测试。

















