Java 使用InOrder验证更改了的List对象的方法调用

Java 使用InOrder验证更改了的List对象的方法调用,java,mockito,Java,Mockito,我有一个回调接口,它有一个接受列表对象的方法。我想使用inoorder来验证回调方法的调用次数是否正确,调用顺序是否正确,参数是否正确 问题是Mockito似乎感到困惑,因为我正在将相同的List对象传递到方法中,并在调用之间对其进行修改。调用inoder.verify()时,我希望在执行该方法调用时验证列表对象的值 代码: public class Test { @Test public void test() { Callback callback = Moc

我有一个回调接口,它有一个接受
列表
对象的方法。我想使用
inoorder
来验证回调方法的调用次数是否正确,调用顺序是否正确,参数是否正确

问题是Mockito似乎感到困惑,因为我正在将相同的
List
对象传递到方法中,并在调用之间对其进行修改。调用inoder.verify()时,我希望在执行该方法调用时验证
列表
对象的值

代码:

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]);
列表
对象的副本传递到每个回调方法调用中会使测试通过。但我不想每次调用该方法时都创建一个新的
列表

我查看了可以传递到
Mockito.mock()
中的
MockSettings
对象,但没有看到任何有用的内容

和我的相似。但该解决方案并不验证传递到每个方法调用中的集合的内容——它只验证传递到其中的某个集合的事实(
anyCollectionOf()
)。它验证最终结果,但不验证单个调用

似乎提供了一个潜在的解决方案。它使用
ArgumentCaptor
捕获传递到方法中的对象。但是,当我使用它来验证第一个方法调用时,在对其进行了所有修改之后,它返回
List
对象,因此测试失败。我需要它返回一个
List
对象,其状态与
List
对象在该精确调用时的状态匹配

ArgumentCaptor argument=ArgumentCaptor.forClass(List.class);
inoorder.verify(callback.foo(Mockito.eq(“name1”),argument.capture());
assertEquals(Arrays.asList(),argument.getValue())//失败:“预期:但是:
inoorder.verify(callback.foo(Mockito.eq(“name2”),argument.capture());
assertEquals(Arrays.asList(“value”)、argument.getValue();
如何在不牺牲任何粒度的情况下通过此测试?

提供了一个解决方案

基本上,为该方法创建一个自定义的
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);
}
@测试
公开无效测试(){
Callback Callback=Mockito.mock(Callback.class);
最终列表listParameters=new ArrayList();
Mockito.doAnswer(新答案(){
公共Void应答(invocationmock调用)抛出可丢弃的{
List=(List)invocation.getArguments()[1];
添加(新的ArrayList(list));
返回null;
}
}).when(callback).foo(Mockito.anyString(),Mockito.anyList());
{
List List=new ArrayList();//注意:在我的实际用例中,从单元测试中无法访问此列表对象
callback.foo(“name1”,list);
增加(“价值”);
callback.foo(“name2”,list);
}
inoorder inoorder=Mockito.inoorder(回调);
inoorder.verify(callback.foo(Mockito.eq(“name1”),Mockito.anyList());
inoorder.verify(callback.foo(Mockito.eq(“name2”),Mockito.anyList());
Assert.assertEquals(Arrays.asList(
Arrays.asList(),
Arrays.asList(“值”)
),列表参数);
}
提供了一个解决方案

基本上,为该方法创建一个自定义的
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);
}
@测试
公开无效测试(){
Callback Callback=Mockito.mock(Callback.class);
最终列表listParameters=new ArrayList();
Mockito.doAnswer(新答案(){
公共Void应答(invocationmock调用)抛出可丢弃的{
List=(List)invocation.getArguments()[1];
添加(新的ArrayList(list));
返回null;
}
}).when(callback).foo(Mockito.anyString(),Mockito.anyList());
{
List List=new ArrayList();//注意:在我的实际用例中,从单元测试中无法访问此列表对象
callback.foo(“name1”,list);
增加(“价值”);
callback.foo(“name2”,list);
}
inoorder inoorder=Mockito.inoorder(回调);
inoorder.verify(callback.foo(Mockito.eq(“name1”),Mockito.anyList());
inoorder.verify(callback.foo(Mockito.eq(“name2”),Mockito.anyList());
Assert.assertEquals(Arrays.asList(
Arrays.asList(),
Arrays.asList(“值”)
),列表参数);
}

如果您打算在我建议的副本中使用我的anser,您可以在捕获功能中创建列表的副本。然后在验证阶段,您可以检查这些副本是否正确。谢谢@SpaceTrucker。这不理想,但它可以工作。如果您打算在我建议的副本中使用我的anser我,你可以在捕获功能中创建列表的副本。然后在验证阶段,你可以检查这些副本是否正确。谢谢@SpaceTrucker。不理想,但它可以工作。如果你说“不理想”“,你认为哪一个更好?@SpaceTrucker如果我的原始代码能够工作,那就更好了。”。这个解决方案冗长,读起来不好。如果你说“它不理想”,你认为什么更好?@SpaceTrucker如果我的原始代码有效,那会更好。该解决方案冗长,读起来不好。