匹配不带ArgumentCaptor

时间:2018-04-25 15:02:56

标签: java testing junit mockito hamcrest

我必须测试一个使用可变对象的方法

private final List<LogMessage> buffer;
...
flushBuffer() {
  sender.send(buffer);
  buffer.clear();
}

我需要测试它是否发送具有确切大小的缓冲区 ArgumentCaptor不适用,因为在断言时捕获的集合是清晰的。

是否有一种匹配器可以重复使用Hamcrest的hasSize()并在方法调用时及时检查?

我更喜欢这个假设的collectionWhich匹配器:

bufferedSender.flushBuffer();
verify(sender).send(collectionWhich(hasSize(5)));

3 个答案:

答案 0 :(得分:2)

David的想法的轻量级替代方法:使用Answer在通话时制作副本。未经测试的代码,但这应该非常接近:

final List<LogMessage> capturedList = new ArrayList<>();
// This uses a lambda, but you could also do it with an anonymous inner class:
// new Answer<Void>() {
//   @Override public Void answer(InvocationOnMock invocation) { /* ... */ }
// }
when(sender.send(any())).thenAnswer(invocation -> {
  List<LogMessage> argument = (List<LogMessage>) invocation.getArguments()[0];
  capturedList.addAll(argument);
});
bufferedSender.flushBuffer();
assertThat(capturedList).hasSize(5);

答案 1 :(得分:2)

Jeff Bowman的答案很好,但我认为我们可以通过在Answer对象本身内联断言来改进它。它避免了创建不必要的复制对象和其他局部变量。

除了我们需要复制自定义对象的状态(通过执行它的深层复制)之外,这种方式更简单。实际上,它不需要任何自定义代码或库来执行副本,因为断言是在运行中完成的。

在Java 8中,它会给出:

import  static org.mockito.Mockito.*;

when(sender.send(any())).thenAnswer(invocation -> {
  List<LogMessage> listAtMockTime = invocation.getArguments()[0];
  Assert.assertEquals(5, listAtMockTime.getSize());
});
bufferedSender.flushBuffer();

请注意InvocationOnMock.getArgument(int index)返回一个无界通配符(?)。因此,调用者不需要强制转换,因为返回的类型是由目标定义的:这里是声明的变量,我们为其分配结果。

答案 2 :(得分:1)

您将遇到与ArgumenCaptor相同的问题,因为verify()方法会在执行后检查调用对象的状态。不执行捕获以仅在调用时保持状态 因此,对于一个可变对象,我认为更好的方法是不使用Mockito而是创建Sender类的存根,您可以在其中捕获集合的实际大小,同时调用send()

这是一个示例存根类(当然可以丰富/适应的最小示例):

class SenderStub extends Sender {

    private int bufferSize;
    private boolean isSendInvoked;

    public int getBufferSize() {
        return bufferSize;
    }

   public boolean isSendInvoked(){
      return isSendInvoked;
   }

    @Override
    public void send(List<LogMessage> buffer ) {
        this.isSendInvoked = true;
        this.bufferSize = buffer.size();
    }    
}

现在,您可以检查是否已调用发件人以及其大小(甚至更多)。

所以放下Mockito来创建这个模拟并验证它的行为:

SenderStub sender = new SenderStub();
MyClassToTest myClass = new MyClassToTest(sender);
// action
myClass.flushBuffer();
// assertion
Assert.assertTrue(sender.isInvoked()); 
Assert.assertEquals(5, sender.getBufferSize());