3

可以使用模拟对象或不使用模拟对象来测试方法。当不需要时,我更喜欢没有模拟的解决方案,因为:

  1. 它们使测试更难理解。
  2. 在重构之后,如果它们是用 mock 实现的,那么修复 junit 测试是很痛苦的。

但我想问问你的意见。这里测试的方法:

public class OndemandBuilder  {

    .... 
    private LinksBuilder linksBuilder;    
    ....

    public OndemandBuilder buildLink(String pid) {

        broadcastOfBuilder = new LinksBuilder(pipsBeanFactory);
        broadcastOfBuilder.type(XXX).pid(pid);
        return this;

    } 

用模拟测试:

@Test
public void testbuildLink() throws Exception {

    String type = "XXX";
    String pid = "test_pid";

    LinksBuilder linkBuilder = mock(LinksBuilder.class);
    given(linkBuilder.type(type)).willReturn(linkBuilder);

    //builderFactory replace the new call in order to mock it
    given(builderFactory.createLinksBuilder(pipsBeanFactory)).willReturn(linkBuilder);

    OndemandBuilder returnedBuilder = builder.buildLink(pid);

    assertEquals(builder, returnedBuilder); //they point to the same obj
    verify(linkBuilder, times(1)).type(type);
    verify(linkBuilder, times(1)).pid(pid);
    verifyNoMoreInteractions(linkBuilder);
}

方法 buildLink 中的 returnedBuilder obj 是“this”,这意味着 builder 和 returnedBuilder 不能不同,因为它们指向内存中的同一个对象,因此 assertEquals 并没有真正测试它是否包含方法 buildLink 设置的预期字段(这是pid)。

我已按如下方式更改了该测试,但未使用模拟。下面的测试断言我们想要测试的是构建器包含一个不为空的 LinkBuilder 并且 LinkBuilder pid 是预期的。

@Test
public void testbuildLink() throws Exception {
    String pid = "test_pid";

    OndemandBuilder returnedBuilder = builder.buildLink(pid);

    assertNotNull(returnedBuilder.getLinkBuilder());
    assertEquals(pid, returnedBuilder.getLinkBuilder().getPid());
}

除非必要,否则我不会使用模拟,但我想知道这是否有意义,或者我误解了模拟测试的方式。

4

2 回答 2

1

在编写单元测试时,模拟是一个非常强大的工具,简而言之,你在类之间存在依赖关系,并且你想测试一个依赖于另一个的类,你可以使用模拟对象来限制你的测试范围,这样你就可以只测试您要测试的类中的代码,而不是它所依赖的那些类。我没有必要进一步解释,我强烈建议您阅读 Martin Fowler 出色的作品Mocks Aren't Stubs以全面介绍该主题。

在您的示例中,没有模拟的测试肯定更干净,但您会注意到您的测试练习代码在OndemandBuilderLinksBuilder类中。这可能是您想要做的,但这里的“问题”是如果测试失败,可能是由于这两个类中的任何一个的问题。在您的情况下,由于代码OndemandBuilder.buildLink很少,我会说您的方法是可以的。但是,如果此函数中的逻辑更复杂,那么我建议您以一种不依赖于方法行为的方式对该方法进行单元测试LinksBuilder.type。这是模拟对象可以帮助您的地方。

假设我们确实想OndemandBuilder.buildLink独立于LinksBuilder实现进行测试。为此,我们希望能够将linksBuilder对象替换OndemandBuilder为模拟对象——通过这样做,我们可以精确控制调用该模拟对象返回的内容,从而打破对LinksBuilder. 这就是依赖注入技术可以帮助您的地方 - 下面的示例显示了我们如何修改OndemandBuilder以允许linksBuilder用模拟对象替换(通过在构造函数中注入依赖项):

public class OndemandBuilder {

    .... 
    private LinksBuilder linksBuilder;    
    ....

    public class OndemandBuilder(LinksBuilder linksBuilder) {
        this.linksBuilder = linksBuilder;
    }

    public OndemandBuilder buildLink(String pid) {

        broadcastOfBuilder = new LinksBuilder(pipsBeanFactory);
        broadcastOfBuilder.type(XXX).pid(pid);
        return this;

    } 
}

现在,在您的测试中,当您创建OndemandBuilder对象时,您可以创建 的模拟版本LinksBuilder,将其传递给构造函数,并控制其行为方式以达到您的测试目的。通过使用模拟对象和依赖注入,您现在可以OndemandBuilder独立于LinksBuilder实现正确地进行单元测试。

希望这可以帮助。

于 2013-07-02T15:33:03.043 回答
0

这一切都取决于您对单元测试的理解。

因为当您尝试对一个类进行单元测试时,这意味着您不必担心底层系统/接口。您假设它们工作正常,因此您只是模拟它们。当我说你在假设时,意味着你正在单独对底层接口进行单元测试。

因此,当您在没有模拟的情况下编写 JUnit 时,本质上您是在进行系统或集成测试。

但是要回答您的问题,两种方式都有其优点/缺点,理想情况下,系统应该两者兼有。

于 2013-07-02T15:29:48.557 回答