我试图按如下方式存根我的mock,
when(someMock.someMethod(Matchers.<SomeInterface<MyType>> any()).thenReturn(someValue);
,
when(someMock.someMethod(Matchers.<SomeInterface<Map<String,Boolean>>> any()).thenReturn(someOtherValue);
我注意到第二个存根覆盖了第一个。所以即使调用someMock.someMethod(new SomeInterfaceImplementation());
someOtherValue
我不知道这里少了什么。我试着在Mockito文档中寻找这个特定的用例,但我没有找到任何东西。
由于类型擦除,Mockito将无法在运行时确定传递给someMock.someMethod
的SomeInterface<T>
实例的通用参数。任何对someMethod
的调用都将显示为使用没有泛型参数的SomeInterface
实例。
JavaDoc for Matchers用一个警告暗示了这个问题(尽管它没有解释原因):
any族方法不做任何类型检查,这些只是为了避免在代码中强制转换。如果要执行类型检查,请使用isA(Class)方法。然而,在未来的主要版本中,这可能会改变(可以添加类型检查)。
还有其他一些辅助方法,如anyListOf
,它们似乎处理集合的泛型参数,但这些方法只是为了方便而存在,实际上并不会检查泛型类型:
anyList()的通用友好别名。它是@SuppressWarnings("unchecked")的替代方法,可以保持代码中没有编译器警告。
任意列表或null
这个方法不做任何类型检查,它只是在你的代码中避免强制类型转换。然而,在未来的主要版本中,这可能会改变(可以添加类型检查)。
因为any()
匹配器将匹配任何对象,而不考虑类型和泛型参数,所以第二次使用any()
调用when()
将覆盖第一次调用。这个问题的唯一解决方案是对两种类型的调用只调用when(...)
一次,并进行某种运行时检查以确定结果应该是什么(例如,在传递给thenAnswer
的Answer
中)。
例如,假设我们想模拟下面的类(实际上不值得模拟,但请原谅我):
public class SomeClass {
public <T> T getFirst(List<T> list) {
return list.get(0);
}
}
getFirst
方法与您的类有相同的问题,因为使用any
/isA
/anyList
/anyListOf
匹配器将无法区分List<String>
和List<Integer>
。
这里有一种方法来模拟andAnswer
:
@Test
public void testGetFirst() {
SomeClass mock = mock(SomeClass.class);
when(mock.getFirst(Matchers.<List<?>>any())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) {
List<?> list = (List<?>) invocation.getArguments()[0];
// Inspect the contents of the list to know which type to return
Object first = list.get(0);
if (first instanceof String) {
return "Z";
} else if (first instanceof Integer) {
return 1000;
} else {
return null;
}
}
});
String firstString = mock.getFirst(Arrays.asList("A", "B", "C"));
System.out.println(firstString); // prints Z
Integer firstInteger = mock.getFirst(Arrays.asList(1, 2, 3));
System.out.println(firstInteger); // prints 1000
}
这很难看,而且可能很难实现,这取决于您实际使用泛型类型的方式,但我相信这是解决运行时缺乏泛型类型信息的唯一方法。