
本文详解 Mockito 中 doReturn() 与 when() 在 Spy 对象上的关键差异,指出常见括号缺失导致的 UnfinishedStubbingException 错误,并提供规范写法、使用场景对比及实战示例。
本文详解 mockito 中 `doreturn()` 与 `when()` 在 spy 对象上的关键差异,指出常见括号缺失导致的 `unfinishedstubbingexception` 错误,并提供规范写法、使用场景对比及实战示例。
在使用 Mockito 对 @Spy(部分模拟对象)进行行为 stub 时,when(...).thenReturn(...) 和 doReturn(...).when(...) 虽然目标一致,但底层机制与适用场景截然不同——而你遇到的 UnfinishedStubbingException,几乎总是源于 doReturn() 的语法结构误写,而非功能失效。
? 根本原因:doReturn() 是“动作先行”语法,必须严格遵循链式调用结构
doReturn() 属于 void-returning 或副作用方法 的 stub 工具,其设计初衷是绕过真实方法执行(尤其适用于 void 方法、final 方法或 spy 对象中需避免触发原始逻辑的场景)。它的标准语法为:
doReturn(returnValue)
.when(mockOrSpyObject) // ← 注意:这里只传入 mock/spy 实例,不带括号!
.methodCall(arguments); // ← 真实方法调用(含参数匹配器)而你在代码中写的是:
doReturn(Optional.of(user1)).when(userRepositoryMock.findById(anyString())); // ❌ 错误!
这行代码实际将 userRepositoryMock.findById(anyString()) 作为 when(...) 的参数执行了——此时 findById() 是真实调用(因是 @Spy),且 anyString() 未被正确绑定到 stub 上, Mockito 无法识别 stub 声明的终点,于是抛出 UnfinishedStubbingException。
✅ 正确写法应明确分离“目标对象”和“目标方法”:
doReturn(Optional.of(user1))
.when(userRepositoryMock) // ← 仅传 spy 实例(无括号、无方法调用!)
.findById(anyString()); // ← 方法名 + 参数匹配器,构成完整 stub 声明✅ 何时该用 when()?何时必须用 doReturn()?
| 场景 | 推荐语法 | 原因 |
|---|---|---|
| 普通 mock 对象(@Mock)上 stub 非 void 方法 | when(mock.method()).thenReturn(...) | 简洁、直观、类型安全 |
| @Spy 对象上 stub 可能触发真实逻辑的方法(如数据库查询、外部调用) | doReturn(...).when(spy).method(...) | 避免 when() 先执行真实方法导致 NPE/异常/副作用 |
| stub void 方法(如 save(), delete()) | doNothing().when(spy).save(...) 或 doThrow(...).when(spy).delete(...) | when() 不支持 void 方法 |
? 小技巧:对 @Spy,优先考虑 doReturn() ——除非你确认该方法调用安全且无副作用。
? 完整可运行示例(JUnit 5 + Mockito 4+)
@ExtendWith(MockitoExtension.class)
class UserUnitTest {
@Spy
private UserRepository userRepositoryMock;
@Test
void testFindUserByIdWithDoReturn() {
User user1 = new User("u1", "Alice");
// ✅ 正确:doReturn + when(spy) + method call
doReturn(Optional.of(user1))
.when(userRepositoryMock)
.findById(anyString());
// 触发 stub
Optional<User> result = userRepositoryMock.findById("any-id");
assertThat(result).isPresent().get().isEqualTo(user1);
}
}⚠️ 注意事项与最佳实践
- 括号是关键:when(spy) 后不能跟 (),否则 Mockito 会尝试执行方法 → 报错或副作用;
- 参数匹配器一致性:anyString() 等需在 .method(...) 中使用,不可提前计算;
- 不要混用:对同一方法多次 stub,后声明的会覆盖前声明的(无论 when 还是 doReturn);
- IDE 提示友好:现代 IDE(IntelliJ)会在 doReturn(...).when(...) 后自动补全 .method(),善用此提示可避免语法错误。
掌握 doReturn() 的“对象先行、方法后置”范式,不仅能解决当前报错,更是安全操控 @Spy 行为的核心能力。记住:when(mock).method() 是声明目标,when(mock.method()) 是危险执行——这是 Mockito stub 语法的分水岭。

















