我有一个简单的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", "!!!"));
}
Comparison Failure:
Expected :bar.action([Hello, World, !!!]);
Actual :bar.action([]);
当然,注释掉 strings.clear()
可以让 Mockito 正确验证。
我可以通过创建一个新列表传递给 Bar
来解决这个问题,例如:
public void processStrings() {
bar.action(new ArrayList<>(strings));
strings.clear();
}
最佳答案
这里不太确定,但这似乎是 Mockito 的限制(但不要引用我的话)。起初我认为可以使用 ArgumentCaptor
来解决这个问题,但它捕获相同的实例,当您验证它时,它已经被修改了。
根据this answer您可以在调用方法时使用 Answer
来验证参数。由于 Bar::action
的返回类型为 void,因此对其进行 stub 需要稍微不同的方法。 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
block 中,当列表传递给模拟时,您可以访问该列表。断言可以在那里完成,或者您可以存储参数并稍后再进行。我想说这个解决方案不是很优雅。
关于java - Mockito 使用更改的列表进行验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43601088/