【问题标题】:How to mock a builder with mockito如何用 mockito 模拟 builder
【发布时间】:2011-12-14 09:11:56
【问题描述】:

我有一个建造者:

class Builder{
     private String name;
     private String address;
     public Builder setName(String name){
         this.name = name;
         return this;
    }
     public Builder setAddress(String address){
         this.address = address;
         return this;
    }

}

在 mockito 中模拟构建器会给我每个方法的 null。那么有没有一种简单的方法可以让构建器在每次函数调用时返回自身,而无需使用when().thenReturn 模拟每个函数本身。

【问题讨论】:

  • 你真的需要模拟这个吗?它看起来不像是值得嘲笑的那种依赖。它看起来像“数据”类,而不是“服务”类。我很少发现在没有太多实际行为的情况下创建类很有用。
  • 这只是一个例子,真正的builder有点复杂,在这种情况下不需要测试。
  • 你能把它们分开,这样你有一个“愚蠢的建造者”(不需要模拟),然后是做的服务部分 需要嘲笑吗?

标签: java unit-testing mockito


【解决方案1】:

使用 RETURN_DEEP_STUBS 的问题在于,每次调用方法时都会得到不同的模拟。我认为从您的问题来看,您想使用一个默认的答案,该答案实际上返回了调用它的模拟,对于每个具有正确返回类型的方法。这可能如下所示。请注意,我没有对此进行测试,因此它可能包含拼写错误,但我希望无论如何意图都清楚。

import static org.mockito.Mockito.RETURNS_DEFAULTS;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class SelfReturningAnswer implements Answer<Object> {

    public Object answer(InvocationOnMock invocation) throws Throwable {
        Object mock = invocation.getMock();
        if(invocation.getMethod().getReturnType().isInstance(mock)){
            return mock;
        }
        return RETURNS_DEFAULTS.answer(invocation);
    }
}

然后,当您创建模拟时,将其指定为您的默认答案。这将使您的模拟从它可以的每个方法中返回自己;但是当你调用一个返回类型对 mock 错误的方法时,它的行为就像一个普通的 mock。

像这样创建你的模拟

Builder mockBuilder = mock( Builder.class, new SelfReturningAnswer());

或为此类创建一个常量并编写类似

@Mock(answer = SELF_RETURNING) private Builder mockBuilder;

希望对您有所帮助。

【讨论】:

  • 你确定。我在我的深存根模拟上使用“验证”,它通过了测试。所以每次我使用构建器方法时它都必须返回相同的实例。
  • 是的,我非常确定。如上所述,我为您的 Builder 类编写了一个测试,在那里我使用 RETURNS_DEEP_STUBS 进行了模拟,然后调用了 setAddress 和 setName。我的测试断言从两个方法调用返回的两个模拟是不同的。我的测试通过了。
  • 似乎这个答案描述的是一种流行的方法......这里提到了类似的东西jakegoulding.com/blog/2012/01/09/…
  • @shiv455 以单例为例
【解决方案2】:

从 Mockito 2.0(测试版)开始,RETURNS_SELF 有一个新的默认答案,其行为几乎与David Wallace's answer 相同。 Mockito 文档中的示例:

@Test
public void use_full_builder_with_terminating_method() {
    HttpBuilder builder = mock(HttpBuilder.class, RETURNS_SELF);
    HttpRequesterWithHeaders requester = new HttpRequesterWithHeaders(builder);
    String response = "StatusCode: 200";

    when(builder.request()).thenReturn(response);

    assertThat(requester.request("URI")).isEqualTo(response);
}

请注意,它同时出现在Mockito 类和Answers 枚举中,因此它也与@Mock(answer = RETURNS_SELF) 语法兼容。

【讨论】:

    【解决方案3】:

    您可以使用RETURN_DEEP_STUBS 模拟链接 API。

    如果您知道调用构建器的确切顺序,以下是您将如何使用它的示例:

    Builder b = Mockito.mock(Builder.class, RETURNS_DEEP_STUBS);
    when(b.setName("a name").setAddress("an address")).thenReturn(b);
    assert b.setName("a name").setAddress("an address") == b; // this passes
    

    不幸的是,这不会为您提供一种模拟“所有各种构建器方法”的通用方式,因此它们总是返回这个,看看另一个答案是你需要那个。

    【讨论】:

    • 另外请注意,如果你得到深度存根排序“错误”并转换结果,它会给你一些看起来很奇怪的消息,可能像 java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$851828bd cannot be cast to ...
    • 我要补充一点,verify 在使用 RETURN_DEEP_STUBS 时不起作用
    • 或者,如果使用注释:@Mock(answer = Answers.RETURNS_DEEP_STUBS)Builder b
    猜你喜欢
    • 1970-01-01
    • 2018-10-08
    • 2019-08-22
    • 1970-01-01
    • 2016-05-08
    • 2012-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多