如何在ArgumentMatcher之后使用ArgumentCaptor?

时间:2015-08-10 15:41:03

标签: java mockito matcher

我有一些模拟的通用界面:

public interface RequestHandler {
  public Object handle(Object o);
}

这个模拟的接口应该在单个测试中处理不同的请求。

when(mock.handle(isA(ARequest.class))).thenReturn(new AResponse());
when(mock.handle(isA(BRequest.class))).thenReturn(new BResponse());

但是我想捕获BRequest的传递实例来检查它的所有参数。有可能吗?

现在我只看到一个解决方案:构建一个ArgumentMatcher的可怕扩展。但是,我不喜欢这样,因为我不会看到AssertionError消息。

2 个答案:

答案 0 :(得分:1)

好的,首先,这种方法可以采用这么多不同类型的参数的事实是代码气味。为不同的输入参数返回许多不同类型的响应的方法明显违反了这一点。见One Thing: Extract till you Drop.

  

多年来,作者和顾问(像我一样)一直在告诉我们,功能应该做一件事。他们应该做得好。他们应该只做它。

也就是说,你可以将Partial Mock传递给你的班级。请参阅:Real Partial Mocks

示例:

BRequest bSpy = spy(new BRequest());
when(mock.handle(bSpy))).thenReturn(new BResponse());
verify(bSpy, times(2)).someMethod();

bSpy不会返回null,因为它是BRequest真实实例,但因为它是间谍,它允许你可以在上面打电话进行验证。

答案 1 :(得分:1)

请记住:虽然匹配器用于存根和验证,但ArgumentCaptor仅用于验证。这很简单:

ArgumentCaptor<BRequest> bCaptor = ArgumentCaptor.for(BRequest.class);
when(mock.handle(isA(BRequest.class))).thenReturn(new BResponse());

systemUnderTest.handle(createBRequest());

verify(mock).handle(bCaptor.capture());
BRequest bRequest = bCaptor.getValue();
// your assertions here

但请注意,这也意味着您无法使用ArgumentCaptor来选择响应。这就是部分嘲笑或答案的来源:

when(mock.handle(any())).thenAnswer(new Answer<Object>() {
  @Override public Object answer(InvocationOnMock invocation) {
    Object argument = invocation.getArguments()[0];
    // your assertions here, and you can return your desired value
  }
});

如果您选择ArgumentMatcher,它可能不会是那么可怕,特别是如果您跳过工厂方法并让Mockito的de-camel-casing成为您的描述:

public static class IsAnAppropriateBRequest extends ArgumentMatcher<Object> {
  @Override public boolean matches(Object object) {
    if !(object instanceof BRequest) {
      return false;
    }
    BRequest bRequest = (BRequest) object;
    // your assertions here
  }
}

when(mock.handle(argThat(new IsAnAppropriateBRequest())))
    .thenReturn(new BResponse());