【问题标题】:Unit testing composition单元测试组成
【发布时间】:2015-04-25 10:57:23
【问题描述】:

我们有一个任务 FooTask。我们正在创建一个 Foo 类,从业务登录的角度来看,它有 1 个责任。但事实证明,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) 通过 Foo 的公共 API 测试类 Bar 和 Baz。 我看到了这种方法的一个主要流程:用于组合对象的子对象越多,到达的测试用例就越多。

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

然后呢?我应该让我的 Foo 课程未经测试吗?我应该重复我的测试吗(这导致了我之前的问题——很多测试用例,更糟糕的是,复制粘贴)?或者也许我应该模拟出所有的依赖关系并断言,我在模拟上的正确方法是在调用 Foo 的方法时调用的?

【问题讨论】:

    标签: unit-testing testing composition


    【解决方案1】:

    首先为 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。此外,如果使用实际对象最终会导致您的单元测试需要很长时间才能运行,那么请模拟它们。

    【讨论】:

    • 是的,我知道什么是模拟以及何时使用它,相信我;)无论如何 - 谢谢你的回复。问题实际上是:我应该更喜欢自己测试成分还是通过“主”类来测试?
    • 是的,这主要归结于他们个人的复杂性。由于它们已分解,我会编写单独覆盖它们的测试,然后仅根据需要使用它们来设置 Foo 对象,以便您可以编写覆盖 Foo 中逻辑的测试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多