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

问题描述:

我有一个回调接口,其方法需要List对象。我希望使用InOrder来验证回调方法是否按照正确的顺序被正确的参数调用了适当的次数。使用InOrder验证更改List对象的方法调用

问题是,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

我看着MockSettings对象,你可以通过Mockito.mock(),但没有看到任何可能的帮助。

This question与我的相似。但该解决方案不验证已传递到每个方法调用的集合的内容 - 它仅验证某些集合已传入它的事实(anyCollectionOf())。它验证最终结果,但不是个别调用

This answer似乎提供了一个潜在的解决方案。它使用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()); 

如何在不牺牲任何粒度的情况下通过此测试?

+0

如果您打算在我所建议的副本中使用我的anser,您可以在捕获功能中创建列表副本。之后在验证阶段,您可以检查这些副本是否正确。 – SpaceTrucker

+0

谢谢@SpaceTrucker。不理想,但它的工作原理。 – Michael

question that @SpaceTrucker linked to提供了一个解决方案。

基本上,为该方法创建一个自定义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); 
} 
+0

如果你说“这不理想”,你认为可能会更好吗? – SpaceTrucker

+0

@SpaceTrucker如果我的原始代码工作会更好。解决方案冗长,阅读不好。 – Michael