单元测试代码覆盖率 - 您有100%的覆盖率吗?

cor*_*ore 45 unit-testing code-coverage

您的单元测试是否构成100%的代码覆盖率?是或否,为什么或为什么不呢.

Rom*_*las 57

没有几个原因:

  • 达到100%的覆盖率确实很昂贵,相比之下90%或95%的利益并不明显.
  • 即使有100%的覆盖率,您的代码也不完美.看一下这种方法(实际上它取决于你所讨论的覆盖类型 - 分支覆盖范围,线路覆盖范围 ......):


public static String foo(boolean someCondition) {
    String bar = null;
    if (someCondition) {
        bar = "blabla";
    }
    return bar.trim();
}
Run Code Online (Sandbox Code Playgroud)

和单元测试:

assertEquals("blabla", foo(true));
Run Code Online (Sandbox Code Playgroud)

测试将成功,您的代码覆盖率为100%.但是,如果您添加另一个测试:

assertEquals("blabla", foo(false));
Run Code Online (Sandbox Code Playgroud)

然后你会得到一个NullPointerException.当你在第一次测试中达到100%时,你不必写第二个测试!

一般来说,我认为关键代码必须几乎100%覆盖,而其他代码可以覆盖85-90%

  • +1表示100%代码覆盖率并不意味着完美的测试套件.您需要100%的路径覆盖,这非常困难(在许多情况下也是不可能的). (14认同)
  • 我不是在谈论功能覆盖!在我的示例中,第一个单元测试给出了100%*line*coverage而不是*function*coverage.然而,正如Falaina所说,*路径*覆盖率在这里不是100%(这是非常难以获得的),这就是为什么第二次测试将失败,即使我已经获得了第一次100%*线*覆盖率测试... (7认同)
  • "达到100%覆盖率真的很贵,相比之下,90%或95%"我不同意,这5%很难测试,因为它没有很好的设计(因为可测试是设计的一部分).出于同样的原因,这5%的untestable可能包含比其余的覆盖代码更多的错误,并且我发现很难通过测试覆盖大多数容易出错的代码??! (5认同)
  • 您正在讨论功能覆盖率,衡量在测试期间是否调用程序中的所有功能.我希望这个指标在所有情况下都是100%; 你怎么能相信一个没有至少调用一次代码中所有函数的测试套件? (3认同)

Pat*_*eam 35

对于所有90%覆盖率的测试人员:

这样做的问题是10%难以测试的代码也是包含90%错误的重要代码!这是经过多年TDD后我凭经验得出的结论.

毕竟这是非常简单的结论.这10%难以测试的代码很难测试,因为它反映了棘手的业务问题或棘手的设计缺陷或两者兼而有之.这些确切的原因经常导致错误的代码.

但是也:

  • 100%覆盖的代码随着时间的推移而减少到低于100%,通常会指出错误或至少是一个缺陷.
  • 与合同一起使用的100%覆盖代码是实现接近无错代码的终极武器.代码合同和自动化测试几乎是一回事
  • 在100%覆盖的代码中发现错误时,更容易修复.由于负责该错误的代码已经被测试所涵盖,因此编写新测试来覆盖错误修复并不困难.


And*_*are 21

不,因为在完美的单元测试和实际完成项目之间存在实际的权衡:)


Rob*_*vey 15

在非平凡的系统中获得100%的代码覆盖率很少是实用的.大多数编写单元测试的开发人员都会在90年代中期到高中.

Pex这样的自动化测试工具可以帮助提高代码覆盖率.它的工作原理是搜索难以找到的边缘情况.

  • 这样做的问题是10%难以测试的代码也是包含90%错误的重要代码!这是经过多年TDD后我凭经验得出的结论. (2认同)

mad*_*lep 10

是的我们做到了.

这取决于你使用的语言和框架,虽然它有多容易实现.

我们将Ruby on Rails用于我当前的项目.Ruby非常"可模仿",因为你可以存根/嘲笑你的代码的大块,而不必构建过于复杂的类组合和构造设计,你必须在其他语言中做.

也就是说,我们只有100%的线路覆盖率(基本上是rcov给你的).您仍然需要考虑测试所有必需的分支.

如果您从一开始就将其作为持续集成构建的一部分包含在内,并且如果覆盖率降至100%以下,则打破构建 - 这促使开发人员立即修复它.当然你可以选择其他一些数字作为目标,但如果你刚刚开始新鲜,那么从90%到100%的努力没有多大区别

我们还有一些其他指标,如果它们跨越给定的阈值也会破坏构建(例如圈复杂度,复制),这些指标都在一起并有助于相互加强.

再一次,你必须从一开始就把这些东西保持在一个严格的水平 - 或者设定一些你能击中的目标,然后逐渐加速,直到达到你满意的程度.

这样做会增加价值吗?起初我很怀疑,但我可以诚实地说是的.主要不是因为您已经对代码进行了全面测试(虽然这绝对是一个好处),但更多的是编写易于测试和推理的简单代码.如果你知道你必须拥有100%的测试覆盖率,你就会停止编写过于复杂的if/else/while/try/catch monstrosities和Keep It Simple Stupid.

  • “如果你知道你必须有 100% 的测试覆盖率,你就不再写过于复杂的 if/else/while/try/catch monstrosities”——非常有趣的一点。 (2认同)

Mik*_*vey 9

当我有机会时我做的是在代码的每个分支上插入可以被grep的语句,并且如果它们被击中则记录,这样我就可以进行某种比较以查看哪些语句没有被命中.这是一件苦差事,所以我并不总是很擅长.

我刚刚建立了一个小型UI应用程序,用于慈善拍卖,使用MySQL作为其数据库.因为我真的,真的不希望它在拍卖中断,我尝试了一些新的东西.

由于它是在VC6(C++ + MFC)中我定义了两个宏:

#define TCOV ASSERT(FALSE)
#define _COV ASSERT(TRUE)
Run Code Online (Sandbox Code Playgroud)

然后我洒了

TCOV;
Run Code Online (Sandbox Code Playgroud)

在整个代码中,在我能找到的每条独立路径上,以及每一个例程中.然后我在调试器下运行程序,每次命中时TCOV,它都会停止.我会查看任何明显问题的代码,然后编辑它_COV,然后继续.代码将动态重新编译并继续下一步TCOV.通过这种方式,我慢慢地,费力地消除了足够的TCOV陈述,因此它会"正常"运行.

过了一会儿,我为代码编写了代码TCOV,这显示了我没有测试过的代码.然后我又回去再跑了,确保测试更多我以前没试过过的树枝.我一直这样做,直到TCOV代码中没有任何声明.

这花了几个小时,但在这个过程中我发现并修复了几个错误.我没有办法制定和遵守一个本应彻底的测试计划.我不仅知道我覆盖了所有分支,而且它让我运行时查看每个分支 - 这是一种非常好的代码审查.

因此,无论您是否使用覆盖工具,这都是一种很好的方法来根除错误,否则这些错误会潜伏在代码中,直到更加尴尬的时候.


jri*_*sta 6

我个人认为100%的测试覆盖率在多个级别上存在问题.首先,您必须确保从您编写的单元测试中获得切实的,节省成本的好处.此外,单元测试与任何其他代码一样,都是CODE.这意味着,就像任何其他代码一样,必须验证其正确性并进行维护.额外的时间验证附加代码的正确性,并维护它并使这些测试有效以响应业务代码的更改,增加了成本.实现100%的测试覆盖率并确保您尽可能彻底地测试代码是一项值得称赞的努力,但不惜一切代价实现它......嗯,通常成本太高.

有很多次覆盖错误和有效性检查以覆盖边缘或非常罕见,但绝对可能,例外情况是代码的示例,不一定需要涵盖.考虑到其他业务需求,为实现这种罕见的边缘案件的覆盖而必须投入的时间,精力(以及最终的金钱)通常是浪费的.属性通常是代码的一部分,特别是对于C#3.0,不需要进行测试,因为大多数(如果不是全部)属性的行为完全相同,并且过于简单(单个语句返回或设置.)投入大量资金时间包装单元测试数以千计的物业很可能会更好地投资到其他地方,在那里可以实现更大,更有价值的投资回报.

除了简单地实现100%的测试覆盖率之外,尝试建立"完美"单元存在类似的问题.如今,模拟框架已经发展到了惊人的程度,几乎任何东西都可以被嘲笑(如果你愿意付钱,TypeMock实际上可以模拟任何东西,但是它确实花费了很多.)但是,通常有时候依赖您的代码不是以可模拟的方式编写的(这实际上是.NET框架本身的大部分核心问题.)投入时间来实现测试的适当范围是有用的,但是过多的是时候嘲笑太阳下面的一切和任何东西,添加层次的抽象和界面使其成为可能,再次通常是浪费时间,精力和最终的金钱.

测试的最终目标不应该是实现最终的代码覆盖.最终目标应该是在编写单元测试时实现单位时间内最大的价值,同时尽可能多地覆盖.实现这一目标的最佳方法是采用BDD方法:指定您的关注点,定义您的上下文,并验证正在开发的任何行为(行为......而非单位)的预期结果.


Ric*_*chH 5

不,因为我花了一些时间添加新功能来帮助用户而不是狡猾地编写那些没有多少价值的模糊测试.我说单位测试大事,微妙的东西和脆弱的东西.


Dav*_*uth 5

在一个新项目中,我练习TDD并保持100%的线路覆盖率.它主要通过TDD自然发生.覆盖范围通常值得关注,并且很容易填补.如果我正在使用的覆盖工具提供分支覆盖或其他我注意到的,虽然我从未见过分支覆盖告诉我任何事情,可能是因为TDD首先到达那里.

我保持100%覆盖率的最有力论据(如果你完全关心覆盖范围)是保持100%覆盖率比管理低于100%覆盖率要容易得多.如果你有100%的覆盖范围,滴,你马上知道是什么原因,可以轻松地修复它,因为下降是在你刚刚一直在努力的代码.但是,如果你满足于95%或者其他什么,那么很容易错过报道回归,你将永远重新审视已知的差距.这就是为什么目前的最佳实践要求一个人的测试套件完全通过的确切原因.管理更少,更难,更容易.

在Ruby工作了一段时间,我的态度肯定得到了支持,那里有优秀的测试框架和测试双打很容易.Python中100%的覆盖率也很容易.我可能不得不在具有较少适用工具的环境中降低我的标准.

我希望对遗留项目有相同的标准,但我从未发现将大型应用程序的平庸覆盖率提高到100%覆盖率是不切实际的.我不得不满足于95-99%.返回并覆盖所有旧代码总是太多的工作.这与我的论点相矛盾,即将代码库保持在100%是很容易的.从一开始就保持这个标准会容易得多.