要检查与方法调用中的参数为特定类型的模拟的交互次数,可以执行
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(isA(FirstClass.class));
这将通过调用isA
,因为someMethod
被调用了两次,但只有一次参数FirstClass
然而,当使用ArgumentCaptor时,这种模式似乎是不可能的,即使是为特定的参数FirstClass
这行不通
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
verify(mock, times(1)).someMethod(captor.capture());
表示模拟被调用了不止一次。
是否有任何方法可以在捕获参数以进行进一步检查的同时完成此验证?
我建议使用Mockito的Hamcrest集成为它编写一个好的、干净的匹配器。这允许您将验证与传递的参数的详细检查结合起来:
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
verify(mock, times(1)).someMethod(argThat(personNamed("Bob")));
Matcher<Person> personNamed(final String name) {
return new TypeSafeMatcher<Person>() {
public boolean matchesSafely(Person item) {
return name.equals(item.getName());
}
public void describeTo(Description description) {
description.appendText("a Person named " + name);
}
};
}
匹配器通常导致更可读的测试和更有用的测试失败消息。它们也往往是非常可重用的,并且您会发现自己构建了一个为测试项目量身定制的库。最后,您还可以使用JUnit的Assert.assertThat()
将它们用于正常的测试断言,这样您就可以双重使用它们。
引用文档:
注意
ArgumentCaptor
不做任何类型检查,它只是避免代码中的强制类型转换。然而,这可能会改变(类型)
我不会使用ArgumentCaptor
。这个类捕获(字面上)所有的东西,不管它作为.forClass
参数提供了什么类。
为了实现你想要的,我建议使用Mockito的Answer
接口拦截参数:
private FirstClass lastArgument;
@Test
public void captureFirstClass() throws Exception {
doAnswer(captureLastArgument()).when(mock).someMethod(anInstanceOfFirstClass());
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(mock, times(1)).someMethod(anInstanceOfFirstClass());
//write your desired matchers against lastArgument object
}
private Answer<FirstClass> captureLastArgument() {
return new Answer<FirstClass>() {
@Override
public FirstClass answer(InvocationOnMock invocation) throws Throwable {
TestClass.this.lastArgument = (FirstClass) invocation.getArguments()[0];
return null;
}
};
}
private static Object anInstanceOfFirstClass(){
return Mockito.argThat(isA(FirstClass.class));
}
您可以使用捕获器进行捕获,然后分别验证每种参数类型的调用次数。
// given
ArgumentCaptor<AA> captor = ArgumentCaptor.forClass(AA.class);
CC cc = new CC();
// when
cut.someMethod(new AA());
cut.someMethod(new BB());
cut.someMethod(new BB());
cut.someMethod(cc);
// then
Mockito.verify(collaborator, atLeastOnce()).someMethod(captor.capture());
Mockito.verify(collaborator, times(1)).someMethod(isA(AA.class));
Mockito.verify(collaborator, times(2)).someMethod(isA(BB.class));
Mockito.verify(collaborator, times(1)).someMethod(isA(CC.class));
assertEquals(cc, captor.getValue());
显然,捕获器引用的泛型类型在运行时不会影响任何事情。
我今天也遇到了这个问题。我想我可以简单地做一些像
verify(mock).someMethod(and(isA(FirstClass.class), captor.capture()));
但是我不能让它工作。我最终得到了这样的解决方案:
@Test
public void Test() throws Exception {
final ArgumentCaptor<FirstClass> captor = ArgumentCaptor.forClass(FirstClass.class);
mock.someMethod(new FirstClass());
mock.someMethod(new OtherClass());
verify(eventBus, atLeastOnce()).post(captor.capture());
final List<FirstClass> capturedValues = typeCheckedValues(captor.getAllValues(), FirstClass.class);
assertThat(capturedValues.size(), is(1));
final FirstClass capturedValue = capturedValues.get(0);
// Do assertions on capturedValue
}
private static <T> List<T> typeCheckedValues(List<T> values, Class<T> clazz) {
final List<T> typeCheckedValues = new ArrayList<>();
for (final T value : values) {
if (clazz.isInstance(value)) {
typeCheckedValues.add(value);
}
}
return typeCheckedValues;
}
注意:如果只需要以这种方式捕获一个类,typeCheckedValues
可以简化为:
private static List<FirstClass> typeCheckedValues(List<FirstClass> values) {
final List<FirstClass> typeCheckedValues = new ArrayList<>();
for (final Object value : values) {
if (value instanceof FirstClass) {
typeCheckedValues.add((FirstClass) value);
}
}
return typeCheckedValues;
}