假设我有一个代码来测试
void myMethod()
{
byte []data = new byte[1];
data[0]='a';
output.send(42, data);
data[0]='b';
output.send(55, data);
}
我写了一个测试:
testSubject.myMethod();
verify(output).send(eq(42), aryEq(new byte[]{'a'}));
verify(output).send(eq(55), aryEq(new byte[]{'b'}));
测试将失败,因为方法实现为两个调用重用相同的数组,在方法完成后不可能匹配第一个send
调用的args,因此技术上应该在方法调用之前指定verify语句,就像期待一样。
测试此类方法的正确方法是什么?
答案 0 :(得分:2)
看起来Mockito在这里有点不方便。它检测方法调用并将其记录(使用mock(MyOutput.class, withSettings().verboseLogging());
启用日志记录),但它存储对您传递的数组的引用,因此在您更改数组时会受到影响。然后它认为方法调用是send(42, [98])
而不是send(42, [97])
。
使用它的一种可能方法是使用您提到的期望。例如,你可以使用一个计数器,如果一个调用符合预期,它就会增加它(它真的只是一种解决方法而且非常讨厌):
MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging());
Main subject = new Main(mock);
AtomicInteger correctCallsCounter = new AtomicInteger(0);
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'}));
subject.myMethod();
assertThat(correctCallsCounter.get(), is(2));
它有效,因为在调用发生时以及字节数组尚未更改时会触发doAnswer
。
此解决方法的一大缺点是,它仅适用于void
方法。如果send
会返回"某些内容",那么我目前无法找到解决问题的方法。
好吧,另一个是这显然是一个相当讨厌的解决方法。
所以我建议稍微重构你的代码(使用新的数组),如果可能的话。这样可以避免这些问题。
如果您的send
方法确实会返回某些内容并且您的myMethod
方法会依赖它,那么您通常会以这种方式进行模拟(send
应该返回此字符串示例):
when(mock.send(eq(55), aryEq(new byte[]{'b'}))).thenReturn("something");
为了仍然使用上面提到的解决方法,你可以改变doAnswer
方法来增加你的计数器并返回你的字符串(无论如何你都会嘲笑它,因此它不是那么糟糕):< / p>
doAnswer(invocation -> {
correctCallsCounter.incrementAndGet();
return "something";
}).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
答案 1 :(得分:0)
使用Answer
复制参数的值。
这是一些代码(它不漂亮):
public class TestMyClass
{
private static List<byte[]> mockDataList = new ArrayList<>();
@InjectMocks
private MyClass classToTest;
private InOrder inOrder;
@Mock
private ObjectClass mockOutputClass;
@After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockOutputClass);
}
@Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doAnswer(new Answer()
{
@Override
public Object answer(
final InvocationOnMock invocation)
throws Throwable
{
final byte[] copy;
final byte[] source = invocation.getArgument(1);
copy = new byte[source.length];
System.arraycopy(source, 0, copy, 0, source.length);
mockDataList.add(copy);
return null;
}
}).when(mockOutputClass).send(anyInt(), any(byte[].class));;
inOrder = inOrder(
mockOutputClass);
}
@Test
public void myMethod_success()
{
byte[] actualParameter;
final byte[] expectedFirstArray = { (byte)'a' };
final byte[] expectedSecondArray = { (byte)'b' };
classToTest.myMethod();
actualParameter = mockDataList.get(0);
assertArrayEquals(
expectedFirstArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(42), any(byte[].class));
actualParameter = mockDataList.get(1);
assertArrayEquals(
expectedSecondArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(55), any(byte[].class));
}
}
请注意,参数的值与调用验证分开进行比较,但仍然验证参数的顺序(即'a'数组首先,然后是'b'数组)。