Java 如何用mockito模拟构建器

Java 如何用mockito模拟构建器,java,unit-testing,mockito,Java,Unit Testing,Mockito,我有一个建筑师: 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 = ad

我有一个建筑师:

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()模拟每个函数本身。然后返回
可以用来模拟链接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

不幸的是,这不会给你一个模拟“所有不同的构建器方法”的通用方法,这样它们总是返回这个,另一个答案是你需要它。

使用return\u DEEP\u STUBS的问题是,每次调用一个方法时,你都会得到不同的模拟。从您的问题中,我认为您希望使用一个默认答案,为每个具有正确返回类型的方法实际返回调用它的mock。这可能看起来像下面这样。请注意,我还没有测试过这个,所以它可能包含拼写错误,但我希望在任何情况下,意图都是明确的

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(answer = SELF_RETURNING) private Builder mockBuilder;
希望这会有所帮助。

从Mockito 2.0(beta版)开始,有一个新的默认答案,它的行为几乎与Mockito 2.0相同。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
enum上,因此它也与
@Mock(answer=RETURNS\u SELF)
语法兼容。

您真的需要模拟它吗?它看起来不像那种值得嘲笑的依赖关系。它看起来像“数据”类,而不是“服务”类。我很少发现在没有太多实际行为的地方创建类是有用的。这只是一个例子,真正的构建器有点复杂,在这种情况下不需要测试。你能把它们分开,这样你就有了一个“哑构建器”(不需要模拟),然后是需要模拟的服务部分吗?你确定吗。我在我的深存根模型上使用“验证”,它通过了测试。所以每次我使用builder方法时,它都必须返回相同的实例。是的,我绝对肯定。我为您的构建器类编写了一个测试,如上所述,在这里,我用RETURNS\u DEEP\u stub创建了一个mock,然后调用setAddress和setName。我的测试断言从两个方法调用返回的两个模拟是不同的。我的测试通过了。看来这个答案所描述的是一种流行的方式…这里提到了类似的东西@shiv455 singleton,例如,还有一点需要注意的是,如果你得到的深存根排序“错误”,并抛出结果,它会给你一些奇怪的消息,可能像
java.lang.ClassCastException:org.mockito.internal.creation.jmock.ClassImposterizer$ClassWithSuperclassToWorkAroundCglibBug$$EnhancerByMockitoWithCGLIB$$851828bd不能强制转换到…
我想补充一点,如果使用注释:
Builder b
@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);
}