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

为何Mockito中@MockBean可用,@SpyBean却抛出InvalidUseOfMatchersException?

Spring Data仓库@SpyBean使用Mockito匹配器异常问题解决

问题现象

用@SpyBean标注Spring Data仓库接口(该接口继承自带有实现类BaseRepositoryImpl的BaseRepository,最终继承自Spring Data的Repository)时,使用Mockito参数匹配器(比如eq(true))存根方法会抛出异常:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded

换成@MockBean标注仓库、或者直接传入真实值(比如用true替代eq(true))时,@SpyBean就能正常工作。另外业务场景中需要用any()这类匹配器处理保存操作——因为待保存对象的具体内容无法提前预知。

原因

@SpyBean会生成真实Bean的Spy实例,而Spring Data仓库的动态代理逻辑和Mockito的参数匹配器机制存在冲突。Spring Data通过动态代理生成仓库实现的过程中,方法调用的代理逻辑会干扰Mockito对参数匹配器的计数校验,导致匹配器使用无效。

解决方案

1. 调整Spy对象的存根方式

如果必须使用@SpyBean,针对需要用匹配器的场景,改用doAnswer替代直接的when/doThrow,通过自定义逻辑处理参数匹配:

// 处理保存操作(无法预知对象内容的场景)
doAnswer(invocation -> {
    YourEntity savedEntity = invocation.getArgument(0);
    // 可添加自定义校验逻辑,比如检查实体属性合法性
    return savedEntity;
}).when(mockitoRepository).save(any(YourEntity.class));

// 处理抛异常场景(若doThrow仍报错,改用doAnswer手动抛异常)
doAnswer(invocation -> {
    throw new RuntimeException();
}).when(mockitoRepository).customFindAll(eq(true));

2. 手动创建Spy实例替代@SpyBean

绕过Spring的@SpyBean自动代理,手动基于真实实现类创建Spy,再注入到测试对象中:

@Autowired
private BaseRepositoryImpl baseRepositoryImpl;

private YourRepository mockitoRepository;

@BeforeEach
void setUp() {
    // 基于真实实现类创建Spy
    mockitoRepository = Mockito.spy(baseRepositoryImpl);
    // 将Spy注入到被测试服务(比如通过setter赋值)
    yourService.setRepository(mockitoRepository);
}

3. 存根用真实值,验证时用匹配器

对于Spy对象,存根方法时传入真实值避免触发匹配器异常,在方法调用后通过verify结合匹配器验证参数:

// 存根时用真实值
Mockito.when(mockitoRepository.customFindAll(true)).thenThrow(RuntimeException.class);

// 执行测试逻辑后,验证保存方法的参数(处理无法预知对象的场景)
verify(mockitoRepository).save(argThat(entity -> 
    entity.getStatus().equals(EntityStatus.NEW)
));

内容的提问来源于stack exchange,提问作者KisNiko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:22:45