使用@SpyBean模拟JpaRepository方法调用序列时的异常问题求解
解决JpaRepository SpyBean模拟异常→正常→异常调用序列的问题
你遇到的问题本质是JpaRepository是抽象接口,Mockito生成的Spy代理没有可直接调用的真实方法实现——Spring Data的Repository实现是动态生成的代理类,而doCallRealMethod()只能针对有具体实现的方法生效,所以会报错。
要实现「异常→正常→异常」的调用序列,核心思路是显式委托真实的Repository实例处理第二次调用,而不是依赖doCallRealMethod()。下面是两种可行方案:
方案一:链式调用结合doAnswer委托真实实例
直接通过链式调用定义每次调用的行为,第二次调用显式调用Spring容器中真实的Repository方法:
@Autowired private MyRepository realMyRepository; // 注入真实的Repository实例 @SpyBean private MyRepository mySpyBean; @Test void testBatchDeleteFailureSequence() { // 定义调用序列:第一次抛异常,第二次调用真实方法,第三次抛异常 doThrow(new DataAccessResourceFailureException("数据库调用失败")) .doAnswer(invocation -> { // 委托给真实Repository执行deleteAll return realMyRepository.deleteAll(invocation.getArguments()); }) .doThrow(new DataAccessResourceFailureException("数据库调用失败")) .when(mySpyBean).deleteAll(any()); // 执行你的服务批量处理逻辑 yourService.handleBatchProcess(); // 验证调用次数 verify(mySpyBean, times(3)).deleteAll(any()); }
方案二:用计数器控制调用行为
如果需要更灵活的调用次数控制(比如不确定调用顺序但需要按次数区分),可以用AtomicInteger记录调用次数,在doAnswer中分支处理:
@Autowired private MyRepository realMyRepository; @SpyBean private MyRepository mySpyBean; @Test void testBatchDeleteWithDynamicSequence() { AtomicInteger callCounter = new AtomicInteger(0); doAnswer(invocation -> { int currentCall = callCounter.incrementAndGet(); if (currentCall == 1 || currentCall == 3) { throw new DataAccessResourceFailureException("数据库调用失败"); } else { // 第二次调用走真实逻辑 return realMyRepository.deleteAll(invocation.getArguments()); } }).when(mySpyBean).deleteAll(any()); // 执行服务逻辑 yourService.handleBatchProcess(); // 验证调用次数 verify(mySpyBean, times(3)).deleteAll(any()); }
关键说明
- 必须注入真实的
MyRepository实例:这个实例是Spring Data动态生成的实现类,包含真实的数据库操作逻辑,而@SpyBean生成的是Mockito的代理,本身没有真实实现。 - 避免直接用
doCallRealMethod():对于接口类型的Spy,该方法无法找到可执行的真实方法,必然抛出你遇到的错误。
内容的提问来源于stack exchange,提问作者rabbitvirus
相关产品推荐
相关产品推荐

