如何在莫吉托中设定期望?



假设我有一个代码要测试

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调用的参数,因此从技术上讲,验证语句应该在方法调用之前指定,类似于期望。

测试这些方法的正确方法是什么?

好吧,看起来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方法来增加计数器并返回 String(无论如何您都会嘲笑它,因此它并没有那么糟糕):

doAnswer(invocation -> {
correctCallsCounter.incrementAndGet();
return "something";
}).when(mock).send(eq(42), aryEq(new byte[]{'a'}));

使用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"数组)。

相关内容

  • 没有找到相关文章

最新更新