ArgumentCaptor 无法直接捕获带具体泛型参数的类型(如 List<String>),需用原始类型(如 ArgumentCaptor<List>)声明并手动转型;泛型方法需按实际传入类型分别捕获,@Captor 字段也须用原始类型;复杂场景可用 doAnswer 替代。

Mockito 的 ArgumentCaptor 可以捕获泛型方法的入参,但需注意类型擦除带来的限制——不能直接用 ArgumentCaptor<List<String>> 这类带具体泛型参数的写法,因为 Java 运行时无法识别泛型类型信息。
用原始类型声明 captor,再通过泛型方法获取值
推荐做法是用原始类型(如 ArgumentCaptor<List>)声明,然后在 getValue() 或 getAllValues() 后手动转型。Mockito 不校验泛型,只关心实际运行时对象类型。
- ✅ 正确写法:
ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class); - ❌ 错误写法:
ArgumentCaptor<List<String>> listCaptor = ArgumentCaptor.forClass(List.class);(编译不通过) - 调用
verify(mock).method(listCaptor.capture());后,可用List<String> captured = (List<String>) listCaptor.getValue();安全转型(前提是传入的确实是List<String>)
对泛型接口/方法使用通配符或类型变量捕获
若被测方法签名含泛型参数(如 <T> void process(T item)),ArgumentCaptor 本身不支持泛型类型变量,只能按实际传入的实参类型捕获。
- 例如:调用
mock.process("hello")→ 用ArgumentCaptor<String> - 调用
mock.process(Arrays.asList(1, 2))→ 用ArgumentCaptor<List>,再转型为List<Integer> - 无法用一个 captor 同时捕获多种泛型实参;需按测试场景分别定义
配合 @Captor 注解和泛型字段(需类型提示)
使用 @Captor 时,字段声明仍需用原始类型,否则编译失败。可通过注释或命名体现预期泛型语义。
立即学习“Java免费学习笔记(深入)”;
- // 表示预期捕获 List<User>
@Captor ArgumentCaptor<List> userListCaptor;- 验证后:
List<User> users = (List<User>) userListCaptor.getValue(); - 建议加
assertThat(users).isNotEmpty();等断言,避免空指针或转型异常
替代方案:用 doAnswer + 自定义捕获逻辑(适合复杂泛型场景)
当需要精确匹配泛型结构(如嵌套泛型、通配符边界)或多次调用需区分时,doAnswer 更灵活:
AtomicReference<List<String>> capturedRef = new AtomicReference<>();doAnswer(invocation -> { capturedRef.set((List<String>) invocation.getArgument(0)); return null; })- 优点:绕过类型擦除限制,直接持有强类型引用;缺点:代码略冗长


















