我有一个简单的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();
}
答案 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
块中,您可以在传递给模拟时访问列表。断言可以在那里完成,或者您可以存储参数并在以后执行。我会说解决方案不是很优雅。