【问题标题】:How do I use ArgumentCaptor after ArgumentMatcher?如何在 ArgumentMatcher 之后使用 ArgumentCaptor?
【发布时间】:2015-08-10 15:41:03
【问题描述】:

我有一些通用的接口来模拟:

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 消息。

【问题讨论】:

    标签: java mockito matcher


    【解决方案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());
    

    【讨论】:

    • 这不是我想要的样子,但无论如何谢谢。也许我会使用带有断言的 ArgumentMatcher,像这样:argThat(bRequest().withLength(123).withComment("qwe123"))
    • 请考虑更新您的问题,如果您想明确说明您正在寻找的匹配程度以及提供的答案不足的原因。如果制作一个灵活的匹配器,您可能还喜欢BaseMatcher in Hamcrest 1.3,它有一个方便的describeMismatch 方法,可以为您提供您提到的报告粒度。构建器模式匹配器很聪明,但要小心:像 Mockito 这样的工具包可以依赖于 Hamcrest 记录的假设,即匹配器是不可变的和幂等的。
    【解决方案2】:

    好吧,首先,这个方法可以接受这么多不同类型的参数,这是一种代码味道。为不同的输入参数返回许多不同类型的响应的方法明显违反了这一点。见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真实实例,但因为它是一个间谍,它允许你调用类似 verify 之类的东西。

    【讨论】:

    • 这不是原子代码块的单元测试,它更像是无状态应用程序/单元的功能测试,在传输层上从其他系统单元模拟。所以它是许多消息的非常抽象的“处理程序”。它可以被包装以使其更具体,但我预计不会添加“更多包装器的包装器”。如果它还没有发布,那么它在当时是不可能的。有几种解决方法:1)Mockito.spy 2)使用不同方法的包装器 3)ArgumentMatcher 中的断言
    • @Dmitry Mockito.spy 是我在这里的建议。
    • 值得一提的是,使用部分模拟通常被认为是代码异味
    猜你喜欢
    • 1970-01-01
    • 2012-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多