【问题标题】:Pitfalls of code coverage [closed]代码覆盖的陷阱[关闭]
【发布时间】:2010-10-16 07:10:44
【问题描述】:

我正在寻找代码覆盖的一些不良副作用的真实示例。

我最近在工作中注意到了这种情况,因为有一项实现 100% 代码覆盖率的政策。代码质量肯定在提高,但相反,测试人员似乎在编写更宽松的测试计划,因为“代码完全经过单元测试”。结果,一些逻辑错误设法溜走了。调试它们真的很痛苦,因为“代码已经过完全单元测试”。

我认为这部分是因为我们的工具只进行了语句覆盖。尽管如此,它本来可以更好地度过。

如果有人对代码覆盖率政策有其他负面影响,请分享。我想知道现实世界中还发生了哪些其他“问题”。

提前致谢。

编辑:感谢所有非常好的回复。有几个我会标记为答案,但不幸的是我只能标记一个。

【问题讨论】:

  • 问杰夫——我不认为他有这个问题。我认为上次我听到一个数字时 stackoverflow 的代码覆盖率为 5% :-)
  • 哈哈,我认为不同之处在于 Jeff 的代码可能比我和我认识的人要好得多 =)
  • 测试、构建或安装没有荣耀。只有当事情变得糟糕并且你做对了你的工作时,你才会被注意到。好问题...
  • 这个问题也应该阅读这个特定的代码覆盖率政策。有一个政策是好的。

标签: unit-testing code-coverage


【解决方案1】:

一句话:代码覆盖率告诉你你肯定没有测试过什么,而不是你有什么。

构建有价值的单元测试套件的一部分是找到最重要的高风险代码并提出难题。您要确保将艰难的事情作为优先事项。覆盖率数字没有代码的“重要性”概念,也没有测试质量的概念。

根据我的经验,您将编写的许多最重要的测试都是几乎不增加任何覆盖率的测试(边缘情况会在这里和那里增加一些额外的百分比,但会发现大量错误)。

设置硬且(可能适得其反的)覆盖率目标的问题在于,开发人员可能不得不开始向后弯腰来测试他们的代码。让代码可测试,然后就是折磨。如果您通过出色的测试达到 100% 的覆盖率,那就太棒了,但在大多数情况下,额外的努力是不值得的。

此外,人们开始痴迷/摆弄数字,而不是关注测试的质量。我见过覆盖率超过 90% 的糟糕测试,就像我见过只有 60-70% 覆盖率的优秀测试一样。

再次,我倾向于将覆盖率视为明确尚未测试的指标。

【讨论】:

  • +1 很好地解释了代码覆盖率的“问题”。
  • +1 我总是喜欢给别人投票,即使我有自己的答案。 :-)
  • +1。根据我的经验,40%-70% 的代码覆盖率是一个神奇的数字。在此之下,您对追溯错误没有足够的安全性。除此之外,您最终会编写毫无意义的测试,例如测试 getter 和 setter,这会阻碍而不是帮助开发。过高的代码覆盖率会使重构变得更慢、更难。当你做错事时,你只需要咬你,不要太多,你不能自发。还要考虑无价值测试。如果一个测试不断被打破,但实际上并没有证明太多,删除它!或者..找到一种更好的方式来表达测试
  • 很好的答案! “让代码可测试,然后就是折磨。” :D
【解决方案2】:

仅仅因为存在代码覆盖率并不意味着您实际上正在测试通过函数的所有路径。

例如,这段代码有四个路径:

if (A) { ... } else { ... }
if (B) { ... } else { ... }

但是,只有两个测试(例如,一个 A 和 B 为真,一个 A 和 B 为假)将提供“100% 的代码覆盖率”。

这是一个问题,因为一旦达到神奇的 100% 数字就会停止测试。

【讨论】:

    【解决方案3】:

    根据我的经验,代码覆盖率工具的最大问题是有人可能会成为“高代码覆盖率”等于“良好测试”这一信念的牺牲品。大多数覆盖率工具只提供语句覆盖率指标,而不是条件、数据路径或决策覆盖率。这意味着有可能在如下代码中获得 100% 的覆盖率:

    for (int i = 0; i < MAX_RETRIES; ++i) {
        if (someFunction() == MAGIC_NUMBER) {
            break;
        }
    }
    

    ...无需测试 for 循环的终止条件。

    更糟糕的是,通过简单地调用您的应用程序的测试可能会获得非常高的“覆盖率”,而无需费心验证输出或验证不正确。

    简单地说,代码覆盖率水平肯定表明测试不足,但覆盖率水平表明足够或正确的测试。

    【讨论】:

      【解决方案4】:

      有时极端情况非常罕见,不值得测试,但严格的代码覆盖规则要求您无论如何都要对其进行测试。

      例如,在 Java 中,MD5 算法是内置的,但从技术上讲,可能会抛出“不支持的算法”类型的异常。它永远不会被抛出,并且您的测试必须经历很大的变化才能测试该路径。

      这会浪费很多工作。

      【讨论】:

      • 我实际上曾经在开发机器上看到一个问题,该问题源于 UTF-8 的 UnsupportedEncodingException。这也应该是内置的,但是那台机器安装了 Sun 的 JDK 的 beta 版本,但它不是......
      • 是的,这可能会发生,我承认这一点。我的观点是,当有这么多其他更可能的问题需要测试时,可能不值得进行测试。代码覆盖率会让你走上低效的道路。
      【解决方案5】:

      在我看来,一个团队在衡量代码覆盖率时遇到的最大危险是它奖励大型测试并惩罚小型测试。如果您可以在编写一个覆盖应用程序大部分功能的单个测试和编写十个测试单个方法的小测试之间做出选择,那么仅测量代码覆盖率意味着您应该编写大型测试。

      但是,编写一组 10 个小测试将使您的测试不那么脆弱,并且会比一个大型测试更彻底地测试您的应用程序。因此,通过衡量代码覆盖率,尤其是在测试习惯仍在不断发展的组织中,您通常会设置错误的激励措施。

      【讨论】:

        【解决方案6】:

        我知道这不是对您问题的直接回答,但是...

        任何测试,无论是什么类型,本身都是不够的。单元测试/代码覆盖率适用于开发人员。 QA 仍然需要对整个系统进行测试。业务用户仍然需要对系统进行整体测试。

        反之,QA 会完全测试代码,因此开发人员不应该测试同样糟糕。测试是免费的,不同的测试提供不同的东西。每种测试类型都可能遗漏其他人可能发现的东西。

        就像其他开发一样,不要走捷径进行测试,它只会让错误通过。

        【讨论】:

          【解决方案7】:
          1. 编写目标过于明确的测试用例。
          2. 代码的输入可变性测试不足
          3. 执行了大量的人工测试用例。
          4. 没有专注于因噪音而导致的重要测试失败。
          5. 很难分配缺陷,因为许多组件的许多条件必须相互作用才能执行生产线。

          拥有 100% 覆盖率目标的最坏副作用是花费大量测试开发周期 (75% 以上) 来解决极端情况。这种策略的另一个不良影响是集中在特定的代码行上,而不是解决输入范围。我真的不在乎 strcpy 函数至少运行了一次。我真的很关心它是否与各种各样的输入相冲突。有政策是好的。但任何极端严厉的政策都是不好的。 100% 的代码覆盖率指标对于代码被认为是可靠的既不是必要的,也不是充分的。

          【讨论】:

            【解决方案8】:

            代码覆盖率的最大缺陷之一是人们只谈论代码覆盖率,而没有实际指定他们所谈论的代码覆盖率类型。 C0、C1、C2甚至更高级别的代码覆盖率的特性非常不同,所以只说“代码覆盖率”甚至没有意义。

            例如,实现 100% 的全路径覆盖几乎是不可能的。如果您的程序有n 决策点,则需要 2n 次测试(根据定义,值中的每个位都是一个决策点,因此要实现 100% 的完整路径覆盖率非常简单的功能,只需添加两个ints,您需要 18446744073709551616 测试)。如果你只有一个循环,你已经需要无限多个测试了。

            OTOH,实现 100% 的二氧化碳覆盖率是微不足道的。

            要记住的另一件重要事情是,代码覆盖率不会告诉您测试了哪些代码。它只会告诉你运行的是什么代码!您可以自己尝试一下:采用具有 100% 代码覆盖率的代码库。从测试中删除所有断言。现在代码库仍然有 100% 的覆盖率,但没有测试任何东西!所以,代码覆盖率不会告诉你测试了什么,只告诉你什么没有测试。

            【讨论】:

              【解决方案9】:

              有一些工具,Jumble 就是其中之一,通过更改您的代码以查看您的测试是否因所有不同的排列而失败,从而通过分支覆盖执行分析。

              直接来自他们的网站:

              Jumble 是一个职业级别的突变 协同工作的测试工具 与 JUnit。突变的目的 测试是提供一个测量 测试用例的有效性。一个 对代码执行突变以 进行测试,相应的测试 然后执行案件。如果 修改后的代码未通过测试,然后 这增加了人们对 测试。反之,如果修改 代码通过了测试,这表明 测试不足。

              【讨论】:

              【解决方案10】:

              代码覆盖率没有问题 - 我看到的错误是 100% 的数字。在某些时候,收益递减定律开始发挥作用,测试最后 1% 的成本比测试其他 99% 的成本更高。代码覆盖率是一个有价值的目标,但常识还有很长的路要走。

              【讨论】:

                【解决方案11】:

                !00% 的代码覆盖率意味着经过良好测试的代码完全是一个神话。作为开发人员,我们知道系统的硬/复杂/精细部分,我更愿意看到这些区域经过适当测试,并且只获得 50% 的覆盖率,而不是每行至少运行一次的无意义数字。

                就现实世界的示例而言,我所在的唯一一个覆盖率为 100% 的团队编写了一些我见过的最糟糕的代码。 100% 的覆盖率被用来代替代码审查——结果很糟糕,以至于大多数代码都被丢弃了,即使它通过了测试。

                【讨论】:

                  【解决方案12】:

                  我们有很好的工具来衡量单元测试的代码覆盖率。因此,依靠 100% 的代码覆盖率来表示您已经“完成测试”是很诱人的。这不是真的。

                  正如其他人所提到的,100% 的代码覆盖率并不能证明您已经进行了充分的测试,50% 的代码覆盖率也不一定意味着您没有进行充分的测试。

                  衡量测试执行的代码行数只是衡量标准之一。您还必须测试各种合理的函数输入,以及函数或类的行为取决于其他一些外部状态。例如,某些代码的功能会根据数据库或文件中的数据而有所不同。

                  我最近也写了一篇博客:http://karwin.blogspot.com/2009/02/unit-test-coverage.html

                  【讨论】:

                    【解决方案13】:

                    100% 的代码覆盖率并不意味着您已经完成了 usnit 测试

                    function int divide(int a, int b) {
                        return a/b;
                    }
                    

                    仅通过 1 个单元测试,我就获得了该函数 100% 的代码覆盖率:

                    return divide(4,2) == 2;
                    

                    现在,没有人会争辩说这个 100% 覆盖率的单元代码表明他的功能工作得很好。

                    我认为代码覆盖率是一个很好的元素,可以知道您是否缺少任何明显的代码路径,但我会谨慎使用它。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-09-14
                      • 2012-02-19
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多