Pio*_*zyk 8 java unit-testing pass-by-reference mockito
我们遇到了与Mockito非常讨厌的问题.
码:
public class Baz{
private Foo foo;
private List list;
public Baz(Foo foo){
this.foo = foo;
}
public void invokeBar(){
list = Arrays.asList(1,2,3);
foo.bar(list);
list.clear();
}
}
public class BazTest{
@Test
void testBarIsInvoked(){
Foo mockFoo = mock(Foo.class);
Baz baz = new Baz(mockFoo);
baz.invokeBar();
verify(mockFoo).bar(Arrays.asList(1,2,3));
}
}
Run Code Online (Sandbox Code Playgroud)
这会导致错误消息,如:
Arguments are different! Wanted:
foo.bar([1,2,3]);
Actual invocation has different arguments:
foo.bar([]);
Run Code Online (Sandbox Code Playgroud)
刚刚发生了什么:
记录的Mockito 参考来list,而不是副本list,所以在上面的的Mockito验证码对修改后的版本(空单,[])而不是调用过程中实际传递的一个([1,2,3])!
题:
除了做一个像下面这样的防御性副本之外,对这个问题有没有优雅和干净的解决方案(实际上有帮助,但我们不喜欢这个解决方案)?
public void fun(){
list = Arrays.asList(1,2,3);
foo.bar(new ArrayList(list));
list.clear();
}
Run Code Online (Sandbox Code Playgroud)
我们不希望修改正确的生产代码并降低其性能,只是为了解决测试中的技术问题.
我在这里问这个问题,因为它似乎可能是Mockito的常见问题.或者我们只是做错了什么?
PS.这不是一个真正的代码,所以请不要问为什么我们创建一个列表然后清除它等.在实际代码中我们真的需要做类似的事情:-).
fge*_*fge 12
这里的解决方案是使用定制的答案.两个代码示例:第一个是使用的测试类,第二个是测试.
一,测试类:
private interface Foo
{
void bar(final List<String> list);
}
private static final class X
{
private final Foo foo;
X(final Foo foo)
{
this.foo = foo;
}
void invokeBar()
{
// Note: using Guava's Lists here
final List<String> list = Lists.newArrayList("a", "b", "c");
foo.bar(list);
list.clear();
}
}
Run Code Online (Sandbox Code Playgroud)
在测试中:
@Test
@SuppressWarnings("unchecked")
public void fooBarIsInvoked()
{
final Foo foo = mock(Foo.class);
final X x = new X(foo);
// This is to capture the arguments with which foo is invoked
// FINAL IS NECESSARY: non final method variables cannot serve
// in inner anonymous classes
final List<String> captured = new ArrayList<String>();
// Tell that when foo.bar() is invoked with any list, we want to swallow its
// list elements into the "captured" list
doAnswer(new Answer()
{
@Override
public Object answer(final InvocationOnMock invocation)
throws Throwable
{
final List<String> list
= (List<String>) invocation.getArguments()[0];
captured.addAll(list);
return null;
}
}).when(foo).bar(anyList());
// Invoke...
x.invokeBar();
// Test invocation...
verify(foo).bar(anyList());
// Test arguments: works!
assertEquals(captured, Arrays.asList("a", "b", "c"));
}
Run Code Online (Sandbox Code Playgroud)
当然,能够编写这样的测试需要你能够注入你的"外部对象"足够的状态,以便测试是有意义的...这里相对容易.
| 归档时间: |
|
| 查看次数: |
5075 次 |
| 最近记录: |