You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot测试中Mockito验证mock调用次数不生效问题

问题根因定位

你遇到的「无论传入times()任何值,verify逻辑都不会触发测试失败」的问题,是两个典型的Spring+Mockito配置错误叠加导致的:

  • 自定义配置类未被Spring识别,创建的mock根本没有注册为容器Bean
    你编写的FileSenderServiceMock类没有添加@Configuration注解。Spring默认仅解析带@Configuration注解类中的@Bean方法,哪怕这个类被传入@SpringBootTest的classes参数,也不会被作为配置类加载。这会引发连锁反应:
  1. getFileSenderServiceMock()方法中创建的mock实例,永远不会被注册到Spring容器中
  2. 容器原本扫描加载的FileSenderServiceImpl真实实现会正常初始化,MyServiceImpl内部注入的是真实实现类对象
  3. 你测试类中@Autowired拿到的FileSenderService实例,本质是真实实现类对象(大概率被AOP生成了代理对象)

为什么对真实实现类调用verify不会抛NotAMockException?
部分Mockito版本对非mock对象的代理实例做了兼容处理,识别到不属于自己生成的对象时会直接跳过校验逻辑,不做调用次数校验,也不抛出异常。这就导致不管你给times()传2、999甚至0,测试都会无报错通过。

  • 同类型Bean注入错位,mock实例与业务实际使用的对象不互通
    就算你补上了@Configuration注解,自定义的@Bean生成的mock Bean默认以方法名作为Bean名称,若容器中同时存在自动扫描生成的真实FileSenderServiceImpl Bean,哪怕你给mock加了@Primary,也可能因为Spring注入优先级规则错位,导致:
  1. MyServiceImpl内部实际注入的是真实的FileSenderServiceImpl
  2. 测试类拿到的是独立的mock实例,从来没有收到过业务逻辑的调用
  3. 同样因为代理对象识别兼容逻辑,Mockito直接跳过校验,出现verify失效的现象
修复方案

按顺序调整即可彻底解决问题:

  1. 给FileSenderServiceMock类补上@Configuration注解,确保内部的@Bean方法能被Spring解析
  2. 简化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;
        }
    }
    
  3. 优先使用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));
        }
    }
    
  4. 去掉测试类上空的@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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 19:27:03