有没有办法使用 mockitos ArgumentCaptore 捕获特定类型的列表。这不起作用:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
ArrayList
)。您始终可以使用 List
接口,如果您想表示它是协变的事实,那么您可以使用 extends
:ArgumentCaptor<? extends List<SomeType>>
使用 @Captor annotation 可以避免嵌套泛型问题:
public class Test{
@Mock
private Service service;
@Captor
private ArgumentCaptor<ArrayList<SomeType>> captor;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldDoStuffWithListValues() {
//...
verify(service).doStuff(captor.capture()));
}
}
是的,这是一个通用的泛型问题,而不是特定于模拟的。
ArrayList<SomeType>
没有类对象,因此您不能以类型安全的方式将此类对象传递给需要 Class<ArrayList<SomeType>>
的方法。
您可以将对象转换为正确的类型:
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
这将给出一些关于不安全强制转换的警告,当然,如果不检查元素,您的 ArgumentCaptor 无法真正区分 ArrayList<SomeType>
和 ArrayList<AnotherType>
。
(正如另一个答案中提到的,虽然这是一个通用的泛型问题,但对于 @Captor
注释的类型安全问题,有一个 Mockito 特定的解决方案。它仍然无法区分 ArrayList<SomeType>
和 { 3}。)
编辑:
另请查看 tenshi 的评论。您可以将原始代码更改为此简化版本:
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
注释。此外,强制转换为 Class
是多余的。
Class
并不是多余的。
如果你不害怕旧的 java 风格(非类型安全的泛型)语义,这也可以工作并且很简单:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject).method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
List<String> mockedList = mock(List.class);
List<String> l = new ArrayList();
l.add("someElement");
mockedList.addAll(l);
ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);
verify(mockedList).addAll(argumentCaptor.capture());
List<String> capturedArgument = argumentCaptor.<List<String>>getValue();
assertThat(capturedArgument, hasItem("someElement"));
对于较早版本的junit,您可以执行
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
我在我的 Android 应用程序中测试活动时遇到了同样的问题。我使用了 ActivityInstrumentationTestCase2
,而 MockitoAnnotations.initMocks(this);
没有用。我用另一个类分别解决了这个问题。例如:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
然后,在活动测试方法中:
HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);
CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;
onView(withId(R.id.signInBtn))
.perform(click());
verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();
关于这个确切的问题有一个open issue in Mockito's GitHub。
我找到了一个简单的解决方法,它不会强迫您在测试中使用注释:
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
public final class MockitoCaptorExtensions {
public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
return new CaptorContainer<T>().captor;
}
public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
return ArgumentCaptor.forClass(argumentClass);
}
public interface CaptorTypeReference<T> {
static <T> CaptorTypeReference<T> genericType() {
return new CaptorTypeReference<T>() {
};
}
default T nullOfGenericType() {
return null;
}
}
private static final class CaptorContainer<T> {
@Captor
private ArgumentCaptor<T> captor;
private CaptorContainer() {
MockitoAnnotations.initMocks(this);
}
}
}
这里发生的情况是,我们创建了一个新类 with @Captor
注释并将捕获器注入其中。然后我们只需提取捕获者并从我们的静态方法中返回它。
在您的测试中,您可以像这样使用它:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
或者使用类似于 Jackson 的 TypeReference
的语法:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
它可以工作,因为 Mockito 实际上不需要任何类型信息(例如,与序列化程序不同)。
@Before
方法中使用MockitoAnnotations.initMocks(this)
,而不是使用排除使用另一个运行器的能力的运行器。但是,+1,感谢您指出注释。> captor;
initMocks(this)
应替换为openMocks(this)
,因为第一个已弃用