Mockito报错:匹配器数量超出预期,如何复用已捕获值?
解决Mockito匹配器数量不匹配及值复用问题
首先,你遇到的核心问题是Mockito匹配器的使用范围错误:你在方法参数的内部(Document构造器中)嵌套使用了anyString()、anyInt()等匹配器,Mockito会将这些都视为独立的方法参数匹配器,但updateOne只有3个方法参数,导致匹配器数量(4个)和参数数量(3个)不匹配。同时要实现两个_id值一致,需要在匹配或存根阶段关联两个参数中的值。
方案1:使用ArgumentCaptor+argThat在匹配阶段验证值一致性并复用
这种方式会在Mockito匹配调用时就验证两个_id的值一致,同时捕获值用于后续存根逻辑:
// 定义捕获器,用于捕获第一个参数中的_id字符串 ArgumentCaptor<String> accountIdCaptor = ArgumentCaptor.forClass(String.class); when(mongoCollection.updateOne( // 匹配第一个参数:验证是包含非空_id的Document,同时捕获该_id argThat(filterDoc -> { String id = filterDoc.getString("_id"); if (id != null) { accountIdCaptor.capture(); return true; } return false; }), // 匹配第二个参数:验证$set中的_id和捕获到的第一个_id一致,且funds是整数 argThat(updateDoc -> { Document setDoc = updateDoc.get("$set", Document.class); return setDoc != null && accountIdCaptor.getValue().equals(setDoc.getString("_id")) && setDoc.getInteger("funds") != null; }), // 匹配第三个参数:任意UpdateOptions实例 any(UpdateOptions.class) )).thenAnswer(invocation -> { // 复用捕获到的_id,将第三个参数存入map fundsByAccount.put(accountIdCaptor.getValue(), invocation.getArgument(2)); return UpdateResult.acknowledged(1); // 返回合法的UpdateResult });
方案2:在thenAnswer中直接处理参数验证与值复用
如果不需要在匹配阶段就验证_id一致性,而是在存根逻辑中处理,可以简化代码,同时避免匹配器数量问题:
when(mongoCollection.updateOne(any(Document.class), any(Document.class), any(UpdateOptions.class))) .thenAnswer(invocation -> { // 从第一个参数中获取_id Document filterDoc = invocation.getArgument(0); String accountId = filterDoc.getString("_id"); // 可选:验证第二个参数中的_id与第一个一致 Document updateDoc = invocation.getArgument(1); Document setDoc = updateDoc.get("$set", Document.class); assert setDoc != null && accountId.equals(setDoc.getString("_id")); // 将_id和第三个参数存入map UpdateOptions options = invocation.getArgument(2); fundsByAccount.put(accountId, options); return UpdateResult.acknowledged(1); });
关键说明
- 避免在方法参数的内部结构(如
Document构造、方法调用)中直接使用Mockito匹配器,Mockito的匹配器仅作用于方法的顶层参数。 - 两种方案的区别:方案1在Mockito匹配调用时就会过滤掉
_id不一致的调用,方案2允许调用进入存根逻辑,再进行验证(适合需要记录调用同时验证参数的场景)。
内容的提问来源于stack exchange,提问作者Tanish azad
相关产品推荐
相关产品推荐

