Mockito验证更改列表

时间:2017-04-25 03:29:23

标签: java list collections mockito

我有一个简单的Bar可以对某些字符串执行操作:

public interface Bar {

  void action(List<String> strings);

}

然后我的Foo正在接受测试。它会随着时间的推移建立一个字符串列表,然后最终使用Bar来处理它们。处理完毕后,队列就会被清除。

public class Foo {

  private final Bar bar;
  private final List<String> strings;

  public Foo(Bar bar) {
    this.bar = bar;
    this.strings = new ArrayList<>();
  }

  public void addStrings(String... strings) {
      Collections.addAll(this.strings, strings);
  }

  public void processStrings() {
    bar.action(strings);
    strings.clear();
  }

}

我正在尝试验证使用预期的字符串调用Bar.action。但是,Mockito没有使用实际参数检测到真正的调用,因为在调用Bar.action 的列表随后被清除。

我可能做错了什么,但这对我来说闻起来像个臭虫。这是测试:

@Test
public void shouldVerify() {
    // Given
    Bar bar = mock(Bar.class);
    Foo foo = new Foo(bar);
    foo.addStrings("Hello", "World");
    foo.addStrings("!!!");
    // When
    foo.processStrings();
    // Then
    verify(bar).action(Arrays.asList("Hello", "World", "!!!"));
}
  

比较失败:

     

预期:bar.action([Hello,World,!!!]);

     

实际:bar.action([]);

可以肯定的是,评论strings.clear()可让Mockito正确验证。

我可以通过将新列表传递给Bar来解决这个问题,例如:

public void processStrings() {
  bar.action(new ArrayList<>(strings));
  strings.clear();
}

1 个答案:

答案 0 :(得分:3)

这里不完全确定,但这似乎是Mockito的限制(但不要引用我的话)。起初我认为这可以通过ArgumentCaptor解决,但它会捕获相同的实例,当您验证它时,它已被修改。

根据this answer,您可以使用Answer在调用方法时验证参数。由于Bar::action具有返回类型的void,因此对其进行存根需要稍微不同的方法。 Mockito docs建议使用doX()系列方法。

public void name() {
    Bar bar = mock(Bar.class);
    doAnswer(invocation -> {
        Object[] args = invocation.getArguments();
        List<String> list = (List<String>) args[0];
        assertThat(list.size(), is(3));
        return null;
    }).when(bar).action(anyList());

    Foo foo = new Foo(bar);
    foo.addStrings("Hello", "World");
    foo.addStrings("!!!");
    foo.processStrings();
}

doAnswer块中,您可以在传递给模拟时访问列表。断言可以在那里完成,或者您可以存储参数并在以后执行。我会说解决方案不是很优雅。