是否有一种方法使用mockitos ArgumentCaptore捕获特定类型的列表。这行不通:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
是的,这是一个一般的泛型问题,不是模拟对象特有的。
ArrayList<SomeType>没有类对象,因此不能以类型安全的方式将此类对象传递给需要Class<ArrayList<SomeType>>的方法。
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>。
ArrayList<AnotherType>
(正如在另一个答案中提到的,虽然这是一个一般的泛型问题,但对于@Captor注释的类型安全问题,有一个特定于mockito的解决方案。它仍然不能区分ArrayList<SomeType>和ArrayList<OtherType>。)
@Captor
ArrayList<OtherType>
再看看天姿的注释。您可以将原始代码更改为以下简化版本:
final ArgumentCaptor<List<SomeType>> listCaptor = ArgumentCaptor.forClass((Class) List.class);
嵌套泛型问题可以用@Captor注释来避免:
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())); } }
如果你不害怕旧的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.
我在我的Android应用程序中有同样的测试活动问题。我使用ActivityInstrumentationTestCase2和MockitoAnnotations.initMocks(this);不起作用。 我用另一个类解决了这个问题。例如:< / p >
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();
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"));
基于@tenshi和@pkalinow的评论(也是对@rogerdpack的赞扬),下面是一个创建列表参数捕获器的简单解决方案,同时禁用“使用未经检查或不安全的操作”警告:
@SuppressWarnings("unchecked") final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor = ArgumentCaptor.forClass(List.class);
完整的示例在这里和相应的通过CI构建和测试运行在这里。
我们的团队已经在单元测试中使用了一段时间,这看起来是我们最直接的解决方案。
对于junit的早期版本,您可以这样做
Class<Map<String, String>> mapClass = (Class) Map.class; ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
关于这个问题有一个在Mockito的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); } } }
这里所发生的是,我们创建了一个新类与——@Captor注释,并将捕获器注入其中。然后我们只需提取捕获器并从静态方法中返回它。
在你的测试中,你可以这样使用它:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
或者使用类似Jackson的TypeReference的语法:
TypeReference
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor( new CaptorTypeReference<Supplier<Set<List<Object>>>>() { } );
它可以工作,因为Mockito实际上不需要任何类型信息(例如,与序列化器不同)。