
本文详解 mockito 中使用 mock() 时为何 stubbed 方法在被真实逻辑调用时无效,并提供两种可靠方案:thencallrealmethod() 和 spy(),帮助开发者正确测试依赖内部方法调用的业务逻辑。
本文详解 mockito 中使用 mock() 时为何 stubbed 方法在被真实逻辑调用时无效,并提供两种可靠方案:thencallrealmethod() 和 spy(),帮助开发者正确测试依赖内部方法调用的业务逻辑。
在使用 Mockito 进行单元测试时,一个常见误区是:对 mock 对象的部分方法进行 stub(如 when(...).thenReturn(...)),却期望其未 stub 的其他方法(如 addUser)自动执行真实逻辑。实际上,Mockito.mock(Class) 创建的是一个完全空实现的对象——所有方法默认返回对应类型的默认值(如 boolean 返回 false,Object 返回 null),且不会执行原始类中的任何代码。
以你的测试为例:
@Test
public void AddExistingUser() {
mockedManager = mock(UserManager.class);
when(mockedManager.checkUsername("test")).thenReturn(true);
assertThrows(IllegalArgumentException.class, () -> {
mockedManager.addUser("test", "test"); // ❌ 不会执行真实 addUser 逻辑!
});
}
问题在于:mockedManager.addUser(...) 是一个 stubbed 空方法,它根本不会进入你源码中 if (checkUsername(username)) { ... } 的判断分支,自然也不会触发异常。即使你 stub 了 checkUsername("test"),该 stub 也仅在显式调用该方法时生效;而 addUser 内部对 checkUsername 的调用,因 addUser 本身未被配置为调用真实逻辑,整个流程被 Mockito 拦截并静默返回默认值(void 方法无返回,但 checkUsername 在 addUser 内部调用时实际返回 false),导致条件不成立、异常未抛出。
✅ 正确做法有两种,按场景选择:
方案一:显式调用真实方法(推荐用于局部验证)
使用 thenCallRealMethod() 或 doCallRealMethod() 显式声明需执行真实逻辑的方法:
@Test
public void AddExistingUser() {
mockedManager = mock(UserManager.class);
// Stub 依赖方法
when(mockedManager.checkUsername("test")).thenReturn(true);
// 关键:让 addUser 调用真实实现
doCallRealMethod().when(mockedManager).addUser("test", "test");
assertThrows(IllegalArgumentException.class, () -> {
mockedManager.addUser("test", "test"); // ✅ 执行真实 addUser,内部 checkUsername 返回 true
});
}
⚠️ 注意:doCallRealMethod() 必须在 when(...).thenReturn(...) 之后调用,且 addUser 的参数需与 stub 保持一致(或使用 anyString() 等匹配器)。若 UserManager 构造依赖外部资源(如文件系统),建议在测试前通过 @BeforeEach 初始化其状态(如清空 usersData)。
方案二:使用 spy()(推荐用于集成度更高的行为测试)
spy() 基于真实对象创建“部分模拟”,默认执行所有方法的真实逻辑,仅对明确 stub 的方法进行覆盖:
@Test
public void AddExistingUser() {
// 创建 spy:基于真实 UserManager 实例(需确保构造可执行)
mockedManager = spy(new UserManager()); // ✅ 默认调用真实 addUser 和 checkUsername
// 仅覆盖 checkUsername 行为(注意:spy 必须用 doReturn(...) 语法)
doReturn(true).when(mockedManager).checkUsername("test");
assertThrows(IllegalArgumentException.class, () -> {
mockedManager.addUser("test", "test"); // ✅ 真实 addUser 执行,内部调用 stubbed checkUsername
});
}
? 提示:spy() 要求目标类有可访问的构造函数(通常为 public 无参构造),且需用 doReturn(...).when(spy).method() 替代 when(...).thenReturn(...),否则可能触发真实方法执行(引发副作用)。
总结
- mock() → 全空对象,所有方法默认不执行逻辑,必须显式配置 thenCallRealMethod() 才能触发真实行为;
- spy() → 真实对象代理,默认执行真实逻辑,仅对 doReturn() 等明确 stub 的方法进行拦截;
- 优先使用 spy() 测试含内部方法调用的业务逻辑;若需彻底隔离依赖(如避免文件 I/O),则应在 spy 前注入可控的依赖(如 mock file 字段)或改用 mock() + thenCallRealMethod() + 细粒度 stub。
掌握这两者的本质区别,可从根本上避免“stub 生效但测试失败”的陷阱。











