Spring Boot测试中Mockito验证mock调用次数不生效问题
问题根因定位
你遇到的「无论传入times()任何值,verify逻辑都不会触发测试失败」的问题,是两个典型的Spring+Mockito配置错误叠加导致的:
- 自定义配置类未被Spring识别,创建的mock根本没有注册为容器Bean
你编写的FileSenderServiceMock类没有添加@Configuration注解。Spring默认仅解析带@Configuration注解类中的@Bean方法,哪怕这个类被传入@SpringBootTest的classes参数,也不会被作为配置类加载。这会引发连锁反应:
getFileSenderServiceMock()方法中创建的mock实例,永远不会被注册到Spring容器中- 容器原本扫描加载的
FileSenderServiceImpl真实实现会正常初始化,MyServiceImpl内部注入的是真实实现类对象 - 你测试类中
@Autowired拿到的FileSenderService实例,本质是真实实现类对象(大概率被AOP生成了代理对象)
为什么对真实实现类调用verify不会抛
NotAMockException?
部分Mockito版本对非mock对象的代理实例做了兼容处理,识别到不属于自己生成的对象时会直接跳过校验逻辑,不做调用次数校验,也不抛出异常。这就导致不管你给times()传2、999甚至0,测试都会无报错通过。
- 同类型Bean注入错位,mock实例与业务实际使用的对象不互通
就算你补上了@Configuration注解,自定义的@Bean生成的mock Bean默认以方法名作为Bean名称,若容器中同时存在自动扫描生成的真实FileSenderServiceImplBean,哪怕你给mock加了@Primary,也可能因为Spring注入优先级规则错位,导致:
MyServiceImpl内部实际注入的是真实的FileSenderServiceImpl- 测试类拿到的是独立的mock实例,从来没有收到过业务逻辑的调用
- 同样因为代理对象识别兼容逻辑,Mockito直接跳过校验,出现verify失效的现象
修复方案
按顺序调整即可彻底解决问题:
- 给
FileSenderServiceMock类补上@Configuration注解,确保内部的@Bean方法能被Spring解析 - 简化mock创建逻辑,去掉不必要的存根代码:
@Configuration public class FileSenderServiceMock { @Bean @Primary public FileSenderService fileSenderService() { // 普通mock即可满足需求,不需要开启RETURNS_DEEP_STUBS FileSenderService mock = Mockito.mock(FileSenderService.class); // 去掉手动doNothing存根:void方法的mock默认行为就是无操作,手动存根反而可能触发不必要的报错 return mock; } } - 优先使用Spring Boot Test提供的
@MockBean注解替换容器实现,避免手动写@Bean导致的实例错位问题:@RunWith(SpringRunner.class) @WebAppConfiguration @SpringBootTest(classes = Application.class) public class MyServiceTest{ @Autowired private MyService myService; // @MockBean会自动替换容器中同类型的Bean,保证业务类和测试类拿到的是同一个mock实例 @MockBean private FileSenderService fileSenderService; @Test public void shouldCreateAndSendFiles(){ myService.createAndSendFiles(); // 此时调整times()的参数,测试就会在次数不匹配时正常报错 verify(fileSenderService, times(999)).sendFile(anyString(), any(byte[].class)); } } - 去掉测试类上空的
@ContextConfiguration注解,避免Spring加载非预期的配置类。
快速校验技巧
当你怀疑verify逻辑不生效时,可以先在业务方法调用前,手动触发一次mock方法调用:
// 手动触发一次mock调用 fileSenderService.sendFile("test", "test".getBytes()); myService.createAndSendFiles(); verify(fileSenderService, times(999)).sendFile(anyString(), any(byte[].class));
- 如果此时测试正常报错,说明你之前的mock实例和业务使用的对象是同一个,只是之前的调用次数不匹配
- 如果测试仍然通过,说明你当前verify的对象根本不是Mockito生成的mock实例
内容的提问来源于stack exchange,提问作者Domin0
相关产品推荐
相关产品推荐

