单元测试组成

时间:2015-02-23 23:05:01

标签: unit-testing testing composition

我们有一个任务FooTask。我们正在创建一个Foo类,从业务loginc的角度来看,它有一个责任。但事实证明,FooTask真的很复杂。它由几个步骤/子任务组成,然后可以由Bar / Baz职责表示。这些类在Foo类之外没有真正的意义,它们不会被单独使用。所以他们可能会有一个包装范围或类似的东西。

假设你的Foo类代表FooTask:

public class Foo {
    private final Bar bar;
    private final Baz baz;

    // all-args constructor

    public ReturnType doFooTask(InputParam input) {
        final SubResult subresult = bar.doBarSubTask(input);
        return baz.doBazSubTask(subresult);
    }
}

现在,您将如何测试?我看到两种选择,但不知道哪一种更好:

1)测试类Bar和Baz通过Foo的公共API。 我看到使用这种方法的一个主要流程:用于构成对象的子对象越多,测试用例就越多。

2)自己编写Bar和Baz类的完整测试套件(它们是包私有的,所以一旦我保持适当的测试包层次结构,我就可以毫无问题地测试它们)。

但那又怎么样?我应该离开我的Foo类未经测试吗?我应该重复我的测试(这会导致我之前的问题 - 很多测试用例,更糟糕的是,复制粘贴)?或者也许我应该模拟所有的依赖关系并断言,在调用Foo的方法时调用我的mock上的正确方法?

1 个答案:

答案 0 :(得分:3)

首先为Bar和Baz编写完整的测试套件。

如果Foo非常简单,就像你的例子一样,那么你可能不会为它编写测试。但我不想让测试代码未经测试,所以我会为它编写测试。如果你为Foo编写测试,那么只做测试Foo所需的测试。单元测试方法通常是为Bar和Baz编写接口然后模拟它们,然后你可以使用一些依赖注入技术来传递模拟。如果Bar和Baz有其他依赖关系,或者尝试触摸文件系统或与数据库或类似的东西交谈,或者它们很复杂并且需要大量设置,这绝对是你想要做的方法。

在某些情况下,您不需要这样做。如果Bar和Baz相当简单并且没有外部依赖关系,那么你可以为Foo编写一个只使用Bar和Baz实例的测试。您编写的这些测试将集中在确保用Foo编写的行为正确运行,并且您在考虑Bar和Baz工作正确的情况下编写,因为您已经用单元测试覆盖了它们。你不会编写涵盖大量输入和输出组合的测试,因为你已经为Bar和Baz完成了这项工作,你只是希望得到Foo的分支覆盖并确保它发送在适当的条件下从Bar到Baz的正确信息。如果你曾经嘲笑Bar和Baz,你会写出相同数量的测试。

如果Bar和Baz很复杂,需要大量的设置或者需要模拟或初始化的外部依赖,然后将它们模拟出去,然后用模拟测试Foo。此外,如果使用实际对象最终会在单元测试中花费很长时间才能运行,那么就嘲笑它们。