单元测试Mockito模拟方法的最佳实践

时间:2011-04-20 00:45:46

标签: java junit tdd mockito

假设我们有在A类中测试的方法,它从类B调用方法。为了测试它,我们为B创建了mock,然后验证它是否被调用。验证(...)是否足以进行单元测试或者我需要断言测试方法的实际结果? 以下是澄清我的担忧的简化示例:

public class StringWriterATest {
    StringWriterB b = mock(StringWriterB.class);

    @Test
    public void stringWriterATest() {
        StringBuffer sb = new StringBuffer();
        StringWriterA a = new StringWriterA();
        a.stringWriterB=b;

        a.append(sb);

        ArgumentCaptor<StringBuffer> argument = ArgumentCaptor.forClass(StringBuffer.class);
        verify(b).append(argument.capture());

        assertEquals("StringWriterA", ((StringBuffer)argument.getValue()).toString());

        //do we really need this or above is enough for proper unit test of method a.append(sb); 
        //assertEquals("StringWriterA_StringWriterB", sb);
    }
}


public class StringWriterA {
    public StringWriterB stringWriterB;

    public void append(StringBuffer sb) {
        sb.append("StringWriterA");
        stringWriterB.append(sb);
    }
}

class StringWriterB {
    public void append(StringBuffer sb) {
        sb.append("StringWriterB");
    }
}

此致 最大

3 个答案:

答案 0 :(得分:1)

永远不需要模拟返回值并同时验证对象。

考虑一下:

StringWriterA是受测试的课程。因此,您肯定希望使用断言来验证此类的行为。为此,您模拟了一个依赖项StringWriterB

想要在StringWriterB的测试中测试StringWriterA,因此您的测试中StringWriterB个互动的任何断言都是错误的地方

您必须假设StringWriterB的行为符合预期。您要么正确验证StringWriterA正确调用StringWriterB(使用verify()您要模拟其预期行为并模拟返回值。

如果你模拟,那么验证是隐式的,因为如果没有调用该方法,则不会返回模拟的返回值。

在您的情况下,StringWriterA.append()不会返回任何值,因此只能进行验证。 StringWriterB.append()也可以运行,应该在自己的stringWriterBTest中进行类似的验证测试。

注意:通过测试明确表示很高兴。由于测试方法永远不会在框架之外调用,因此永远不需要输出它们,因此您可以拥有比生产代码方法更长的方法名称。一个很好的约定是:

<underTest>Should<Expected>[When]<Condition>()

stringWriterAShouldAppendConstantAndDelegateToStringWriterB() stringWriterAShouldThrowNullPointerExceptionWhenNullArgument()

如果您的构建中存在测试失败(持续集成),那么您不必寻找出错的方法,方法名称会出现失败,您可以阅读它以确切知道必须修复哪些行为

答案 1 :(得分:0)

在您的示例中,StringWriterB不存储任何状态,append方法可能很容易变为静态。在那种情况下,调用纯粹是副作用,不需要进行测试。

但是,我怀疑你的真实代码要复杂得多。如果有另一个对象访问StringWriterB,那么你可能想要模拟它以防万一有意外的调用。如果您希望将来扩展B,您可能还想添加B的验证 - 可能存储来自追加调用的状态并具有访问者。

要考虑的一件事是对StringWriterA.append()的调用的目的是什么。如果它的工作是附加字符串StringWriterAStringWriterB那么那就是你应该测试的东西,并且不需要模拟。 StringWriterA如何完成这项任务并不重要。但是,如果它的部分工作是调用StringWriterB.append()方法,那么除非你想在A的测试中测试StringWriterB,否则可能需要模拟。

我的经验法则WRT模拟是使用真实物体,直到我没有直接测试的物体的线路变得太毛或太脆。如果我觉得我的测试的很大一部分是在实际测试其他对象,那么嘲笑将是一个好主意。

答案 2 :(得分:0)

首先尝试向某人展示您编写的测试。在我看来很难读。 你无法真正告诉它你正在测试什么行为。您可以在此处找到如何使其更具可读性的简要介绍How to Write Clean, Testable Code 。 使用参数捕获者也是一种气味。可以在tdd blog上找到一些如何避免它的示例。

要回答您的问题verify用于验证类之间的交互用于推动代码设计。结果(如果需要)应由开头的whengiven指定你的考试。

有关如何使用模拟驱动设计的更多信息(whengivenverify,...)以及模拟与存根的不同之处可在此处找到:{{3 }}。该示例使用JMock而不是Mockito来定义模拟,但它非常相似(它是关于概念,而不是您使用的实现和库的细节)。