Java 8下Mockito废弃anyListOf的替代方案及泛型报错解决
问题背景
你遇到的情况是:Mockito的anyListOf()方法已被标记为废弃(将在Mockito 3.0移除,官方说明Java 8的类型推断已不再需要它),但当你用anyList()替代后,在调用泛型方法getDataFromEndpoint的when()语句中,出现了Cannot resolve method 'thenReturn(java.util.List)'的报错。你已经有临时修复方案,但想找到更优解,同时疑惑为什么Java 8的类型推断改进后还是会出错。
为什么Java 8类型推断还会踩坑?
虽然Java 8增强了类型推断能力,但Mockito的anyList()默认返回的是List<Object>。当你在when(serviceUtil.getDataFromEndpoint(anyInt(), anyList(), ...))中使用anyList()时,编译器没办法从当前上下文自动推断出getDataFromEndpoint的泛型参数T具体是什么类型——因为anyList()没有提供足够的类型锚点,导致Mockito匹配的方法签名和你thenReturn传入的List<T>类型不兼容,最终触发了报错。
更优解决方案
1. 显式指定anyList()的泛型类型
这是最直接且类型安全的方式,通过显式泛型调用给anyList()指定具体类型,让编译器和Mockito能精准推断T的类型:
@Test public void getDataTest() { when(dataSetDao.getDataSetList()).thenReturn(getMockDataSetList()); // 显式指定anyList的泛型为Data,补全类型信息 when(serviceUtil.getDataFromEndpoint(anyInt(), ArgumentMatchers.<Data>anyList(), anyString(), any(TypeToken.class), anyString())) .thenReturn(getMockDataList()); List<Data> dataList = noaaDataService.getData(); verify(serviceUtil, times(11)).getDataFromEndpoint(anyInt(), ArgumentMatchers.<Data>anyList(), anyString(), any(TypeToken.class), anyString()); assertEquals(11, dataList.size()); } // 优化getMockDataList,去掉不必要的泛型和强转 private List<Data> getMockDataList() { List<Data> dataList = new ArrayList<>(); for (int i = 0; i < 1; ++i) { dataList.add(new Data()); } return dataList; }
这种方式既不需要修改测试方法的核心逻辑,也消除了强转带来的类型安全隐患,代码可读性也更高。
2. 用类型限定的any()匹配(备选方案)
如果你不想显式指定泛型,也可以用any(List.class)配合类型转换,但这种方式会产生unchecked cast警告,需要添加@SuppressWarnings("unchecked")消除,安全性不如第一种:
@SuppressWarnings("unchecked") @Test public void getDataTest() { when(dataSetDao.getDataSetList()).thenReturn(getMockDataSetList()); when(serviceUtil.getDataFromEndpoint(anyInt(), any(List.class), anyString(), any(TypeToken.class), anyString())) .thenReturn((List<Data>) getMockDataList()); List<Data> dataList = noaaDataService.getData(); verify(serviceUtil, times(11)).getDataFromEndpoint(anyInt(), any(List.class), anyString(), any(TypeToken.class), anyString()); assertEquals(11, dataList.size()); }
3. 优化泛型Mock方法(长期复用方案)
你原本的getMockDataList泛型设计有点冗余,因为实际返回的是List<Data>。如果需要复用这个方法生成不同类型的测试列表,可以改成更通用的版本:
private <T> List<T> getMockDataList(Supplier<T> entitySupplier, int count) { List<T> dataList = new ArrayList<>(); for (int i = 0; i < count; ++i) { dataList.add(entitySupplier.get()); } return dataList; }
调用时直接传入实体的构造方法引用,类型完全安全:
thenReturn(getMockDataList(Data::new, 1));
关于废弃的anyListOf()
Mockito废弃anyListOf(Class<T> clazz)的核心原因是Java 8的目标类型推断可以自动处理泛型匹配,但这个推断需要上下文提供足够的类型信息。而在when()的方法匹配场景中,anyList()本身是无界的泛型方法,无法自动关联到目标方法的泛型参数,所以才需要我们显式指定泛型来补全信息——这就是直接替换会出错的根本原因。
内容的提问来源于stack exchange,提问作者Sadiq Ali

