tdd - 测试正确的东西,避免重复覆盖的技术

标签 tdd code-coverage

考虑以下事件序列:

  • 你写了一个函数A()完成一个工作单元
  • 您为函数 A() 编写了一个测试,以确保不存在错误
  • 你写函数B()使用函数 A()
  • 你为函数 B() 编写了一个测试。确保不存在错误
    4.1 功能测试B()封面功能A()因此,您至少有 2 个测试涵盖了一些相同的功能

  • 问题一:为函数编写测试是否值得 A()开始?
  • 你写了很多代码
  • 你编写了一个巨大的回归测试,端到端测试程序功能
    6.1 这个单一的回归测试有效地重复了绝大多数已经编写的测试

  • 问题二:通过遵循这些步骤,代码包含许多测试,这些测试不止一次地涵盖了同一件事。有没有避免这种情况的技术?

    假设:

    出于这个问题的目的,请假设 B 做了两件事,其中一件是 A
    void performLifeChoice() { // B()
      if (timeIsRight) {
         askForPromotion();   // A()
      } else {
         goBackToSchool();
      }
    }
    

    最佳答案

    我的一般回答是肯定的,值得为 A() 编写单元测试,原因有两个:

    1) 你最终可能会从与 B() 不同的上下文中调用 A(),在这种情况下,你会很高兴知道 A() 正在工作,并避免重复测试。如果您没有单独对 A() 进行测试,您最终会为等效的代码分支重写两次实际上是相同的测试。

    2) 与此相关的是,如果您不单独测试 A(),您最终可能会一直遇到海龟。现在想象一下,一个函数 C() 在它的一个分支中调用 B(),而 D() 调用 C() 等等......如果你遵循只测试更高级别函数的路径,你最终会集成测试必须涵盖越来越多的前提条件。在尽可能少的上下文中测试单个单元,原则上可以避免这个问题。

    我的回答的一个隐含结论是,如果 A() 永远不会从 B() 以外的地方被调用,那么将 A() 设为私有(private)可能是值得的,此时,测试可能会变得“可选”。另一方面,保留该测试可能仍然有用,因为它将帮助您确定当 B() 失败时,是因为您破坏了 A()。用 Kent Beck 的话来说,“如果测试失败,有多少事情会出错?答案越接近 1,测试就越“unit-y”。” - 进行单元测试非常有用,它可以帮助您准确找出代码中哪里出了问题。

    现在你怎么能去测试 B(),而不复制 A() 的测试,增加前置条件?

    这就是 Mocking/Stubbing 或类似技术可以发挥作用的地方。如果您考虑 B() 正在做什么,它实际上并没有执行 A(),它充当“协调器”,将条件输送到各种路径。在我看来,一个充分的测试应该是“当 TimeIsRight 时,B() 应该调用 A()”,A() 提供的答案与 B() 无关,这是 A() 的责任,你的单元测试涵盖。

    在那个框架中,对 B() 的测试应该断言是在 TimeIsRight 时调用 A(),而不是 A() 返回的内容。根据您的代码的具体情况,您可以考虑使 A() 在 B() 中“可替代”,例如通过接口(interface)或通过注入(inject)代替 A() 的函数。

    关于tdd - 测试正确的东西,避免重复覆盖的技术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11910057/

    相关文章:

    go - 测试构造函数被多次调用的地方?

    c# - 使用提取和覆盖模式的 TDD 构造函数中的虚拟方法

    PHPUnit --no-coverage 选项覆盖配置文件

    unit-testing - 在 .net 核心、xUnit 项目中获取代码覆盖率

    ruby - Gem 中的测试需要测试迁移生成器并为测试应用迁移

    sql-server - 如何对持久性进行单元测试?

    tdd - 什么最适合缺陷率跟踪?每个KLOC的缺陷?

    java - 测试 JUnit 和 EclEmma 时遗漏的分支

    perl - 在 Devel::Cover 报告中包含未发现的文件

    eclipse - 无法为 Eclipse 安装 Cobertura 插件