我有一个回调接口,它有一个接受List
对象的方法。我想使用InOrder
来验证回调方法是否以正确的顺序和正确的参数被调用了适当的次数。
问题是,Mockito似乎感到困惑,因为我将同一个List
对象传递到方法中,并在调用之间对其进行修改。当我调用InOrder.verify()
时,我想验证执行该方法调用时List
对象的值。
代码:
public class Test {
@Test
public void test() {
Callback callback = Mockito.mock(Callback.class);
InOrder inOrder = Mockito.inOrder(callback);
{
List<String> list = new ArrayList<String>(); //note: this List object is inaccessible from the unit test in my real use-case
callback.foo("name1", list);
list.add("value");
callback.foo("name2", list);
}
inOrder.verify(callback).foo("name1", Arrays.<String> asList()); //fails here
inOrder.verify(callback).foo("name2", Arrays.asList("value"));
}
interface Callback {
void foo(String name, List<String> list);
}
}
错误消息:
Argument(s) are different! Wanted:
callback.onFoo([]);
Actual invocation has different arguments:
callback.onFoo([value]);
在每次回调方法调用中传递List
对象的副本会使测试通过。但我不想每次调用该方法时都创建一个新的List
。
我查看了可以传递到Mockito.mock()
中的MockSettings
对象,但没有发现任何可能有帮助的内容。
这个问题和我的相似。但是,该解决方案并不验证传递到每个方法调用中的集合的内容——它只验证某个集合被传递到其中的事实(anyCollectionOf()
)。它验证最终结果,但不是单个调用。
这个答案似乎提供了一个潜在的解决方案。它使用ArgumentCaptor
来捕获传递到方法中的对象。但是,当我使用它来验证第一个方法调用时,在对其进行了所有修改后,它会返回List
对象,因此测试失败。我需要它返回一个List
对象,该对象的状态与List
对象在精确调用时的状态相匹配。
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
inOrder.verify(callback).foo(Mockito.eq("name1"), argument.capture());
assertEquals(Arrays.asList(), argument.getValue()); //fails: "expected: <[]> but was: <[value]>
inOrder.verify(callback).foo(Mockito.eq("name2"), argument.capture());
assertEquals(Arrays.asList("value"), argument.getValue());
如何在不牺牲任何粒度的情况下通过此测试?
@SpaceTrucker链接到的问题提供了解决方案。
基本上,为该方法创建一个自定义Answer
,并在每次调用该方法时存储List
参数的副本。然后,验证所有的副本都是你期望的。这并不理想,但它有效。
@Test
public void test() {
Callback callback = Mockito.mock(Callback.class);
final List<List<String>> listParameters = new ArrayList<List<String>>();
Mockito.doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) throws Throwable {
List<String> list = (List<String>) invocation.getArguments()[1];
listParameters.add(new ArrayList<String>(list));
return null;
}
}).when(callback).foo(Mockito.anyString(), Mockito.anyList());
{
List<String> list = new ArrayList<String>(); //note: this List object is inaccessible from the unit test in my real use-case
callback.foo("name1", list);
list.add("value");
callback.foo("name2", list);
}
InOrder inOrder = Mockito.inOrder(callback);
inOrder.verify(callback).foo(Mockito.eq("name1"), Mockito.anyList());
inOrder.verify(callback).foo(Mockito.eq("name2"), Mockito.anyList());
Assert.assertEquals(Arrays.asList(
Arrays.asList(),
Arrays.asList("value")
), listParameters);
}