用mock还是没有测试更好?

时间:2013-07-02 10:29:51

标签: testing mocking

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

  1. 他们让测试更难理解。
  2. 重构之后,修复junit测试是很痛苦的,如果它们已经用模拟实现了。
  3. 但我想问你的意见。这是测试中的方法:

    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不是null,而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());
    }
    

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

2 个答案:

答案 0 :(得分:1)

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

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

假设我们确实希望独立于OndemandBuilder.buildLink实施来测试LinksBuilder。为此,我们希望能够使用模拟对象替换linksBuilder中的OndemandBuilder对象 - 通过这样做,我们可以精确地控制对此模拟对象的调用返回的内容,从而打破依赖性LinksBuilder的实施。这是技术Dependency Injection可以帮助您的地方 - 下面的示例显示了我们如何修改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

希望这有帮助。

答案 1 :(得分:0)

这一切都取决于你对UNIT测试的理解。

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

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

但要回答你的问题,两种方式都有其优点/缺点,理想情况下,系统应该同时具备这两种方法。