如何使用 mockito 捕获特定类型的列表
Posted
技术标签:
【中文标题】如何使用 mockito 捕获特定类型的列表【英文标题】:How to capture a list of specific type with mockito 【发布时间】:2011-08-02 04:02:47 【问题描述】:有没有办法使用 mockitos ArgumentCaptore 捕获特定类型的列表。这不起作用:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
【问题讨论】:
我发现在这里使用具体的列表实现是一个糟糕的主意 (ArrayList
)。您始终可以使用List
接口,如果您想表示它是协变的,那么您可以使用extends
:ArgumentCaptor<? extends List<SomeType>>
【参考方案1】:
使用@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()));
【讨论】:
我更喜欢在@Before
方法中使用MockitoAnnotations.initMocks(this)
,而不是使用排除了使用另一个跑步者能力的跑步者。但是,+1,感谢您指出注释。
不确定这个例子是否完整。我明白了... 错误:(240, 40) java: variable captor may not have been initialized 我喜欢下面的 Tenshi 的回答
我遇到了同样的问题,发现这篇博文对我有点帮助:blog.jdriven.com/2012/10/…。它包括一个在您将注释放在类上之后使用 MockitoAnnotations.initMocks 的步骤。我注意到的一件事是你不能在局部变量中使用它。
@chamzz.dot ArgumentCaptor> 俘虏;已经捕获了一个“SomeType”数组
我通常更喜欢在 Captor 声明中使用 List 而不是 ArrayList:ArgumentCaptor> captor;【参考方案2】:
是的,这是一个通用的泛型问题,而不是 mockito 特定的问题。
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>
和ArrayList<OtherType>
。)
编辑:
还请查看tenshi 的评论。您可以将原始代码更改为这个简化版本:
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
【讨论】:
您展示的示例可以简化,基于 java 对静态方法调用进行类型推断的事实:ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
要禁用 uses unchecked or unsafe operations 警告,请在参数捕获器定义行上方使用 @SuppressWarnings("unchecked")
注释。此外,强制转换为 Class
是多余的。
在我的测试中转换为Class
并不是多余的。【参考方案3】:
如果您不害怕旧的 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.
【讨论】:
您可以在声明前添加@SuppressWarnings("rawtypes") 以禁用警告。【参考方案4】: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"));
【讨论】:
【参考方案5】:基于@tenshi 和@pkalinow 的cmets(也感谢@rogerdpack),以下是创建列表参数捕获器的简单解决方案,它也禁用“使用未经检查或不安全的操作”警告 :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
完整示例 here 和相应的通过 CI 构建和测试运行 here。
我们的团队已经在我们的单元测试中使用了一段时间,这对我们来说似乎是最直接的解决方案。
【讨论】:
【参考方案6】:对于较早版本的junit,可以这样做
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
【讨论】:
【参考方案7】:我在我的 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();
【讨论】:
【参考方案8】:关于这个确切的问题有一个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);
这里发生的是我们创建一个新类 @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 实际上不需要任何类型信息(例如,与序列化程序不同)。
【讨论】:
以上是关于如何使用 mockito 捕获特定类型的列表的主要内容,如果未能解决你的问题,请参考以下文章
模拟无效方法的try catch块并使用EasyMock或Mockito捕获异常