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

时间:2009-09-25 05:01:09

标签: unit-testing code-coverage

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

17 个答案:

答案 0 :(得分:55)

没有几个原因:

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


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

和单元测试:

assertEquals("blabla", foo(true));

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

assertEquals("blabla", foo(false));

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

一般来说,我认为关键代码必须覆盖率接近100%,而其他代码则覆盖率为85-90%

答案 1 :(得分:33)

对所有90%覆盖率测试者:

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

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

但是:

  • 100%覆盖的代码随着时间的推移而减少到低于100%,通常会指出错误或至少是一个缺陷。
  • 与合同一起使用的100%覆盖代码,是实现接近无错代码的终极武器。 Code Contracts and Automated Testing are pretty much the same thing
  • 如果在100%覆盖的代码中发现错误,则更容易修复。由于测试已经涵盖了负责该错误的代码,因此编写新测试来解决错误修复并不困难。

答案 2 :(得分:19)

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

答案 3 :(得分:14)

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

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

答案 4 :(得分:10)

是的,我们这样做。

这取决于您使用的语言和框架,以及实现的容易程度。

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

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

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

如果他们跨越给定的阈值(例如圈复杂度,例如重复),我们还有一些其他指标可以打破构建,这些指标会一起发生并帮助相互加强。

同样,你必须从一开始就把这些东西放在一个严格的工作位置 - 或者设定你可以击中的目标,然后逐渐加速,直到你达到一个你很高兴的水平用。

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

答案 5 :(得分:9)

当我有机会时,我所做的是在代码的每个分支上插入语句,如果它们被命中,那么我可以进行某种比较以查看哪些语句已被命中没被击中。这是一件苦差事,所以我并不总是很好。

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

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

#define TCOV ASSERT(FALSE)
#define _COV ASSERT(TRUE)

然后我撒了

TCOV;

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

过了一会儿,我抓了TCOV的代码,这显示了我没有测试过的代码。然后我又回去再跑了,确保测试更多我以前没试过过的树枝。 我一直这样做,直到代码中没有TCOV语句。

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

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

答案 6 :(得分:6)

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

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

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

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

答案 7 :(得分:5)

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

答案 8 :(得分:5)

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

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

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

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

答案 9 :(得分:4)

我通常将单元测试编写为回归预防方法。当报告我必须修复的错误时,我会创建一个单元测试,以确保它在将来不会重新浮出水面。我可以为功能部分创建一些测试,以确保保持完整(或复杂的部件间交互),但我通常希望错误修复告诉我一个是必要的。

答案 10 :(得分:4)

我通常设法达到93 .. 100%我的报道但我不再瞄准100%。我曾经这样做,虽然它是可行的,但是超出某一点的努力是不值得的,因为通常不需要盲目测试。这方面的好例子可能是以下代码剪切的真正评估分支

public void method(boolean someBoolean) {
    if (someBoolean) {
        return;
    } else {
        /* do lots of stuff */ 
    }
}

然而,重要的是要尽可能接近100%覆盖类的功能部分,因为这些是你的应用程序的危险水域,蠕动的bug和未定义的行为的迷雾当然还有赚钱的跳蚤马戏团。

答案 11 :(得分:4)

来自Ted Neward blog

  

到目前为止,大多数开发者至少听说过,如果不考虑采用Masochistic Testing meme。 NFJS'ers Stuart Halloway和Justin Gehtland成立了一家咨询公司Relevance,它将企业文化标准定为高标准:100%的代码测试覆盖率。

     

Neal Ford报告说,ThoughtWorks也做了类似的陈述,尽管我的理解是客户有时会在实现上述目标的过程中遇到意外障碍。这是雄心勃勃的,但据说古代美洲印第安人的谚语说,

     
    

如果你的箭射向太阳,它会比你瞄准地面飞得越来越远。

  

答案 12 :(得分:2)

是的,我的项目有100%的线路覆盖率。请参阅我对类似question.

的回答

可以获得100%覆盖率,但正如其他人在SO和互联网上的其他地方指出的那样,它可能只是最低限度。当你考虑路径和分支覆盖时,还有很多工作要做。

另一种看待它的方法是尝试使您的代码变得如此简单,以便轻松获得100%的线覆盖率。

答案 13 :(得分:2)

在许多情况下,不值得获得100%的声明覆盖率,但在某些情况下, 值得。在某些情况下,100%的声明覆盖率太远 lax 是一种要求。

要问的关键问题是,“如果软件失败会产生什么影响(产生错误的结果)?”。在大多数情况下,bug的影响相对较低。例如,您可能需要在几天内修复代码并重新运行。但是,如果影响是“有人可能会在120秒内死亡”,那么这将产生巨大的影响,并且您应该拥有批次更多的测试覆盖率而不仅仅是100%的语句覆盖率。

我领导Linux基金会的Core Infrastructure Initiative Best Practices Badge。我们有100%的声明覆盖率,但我不认为这是绝对必要的。很长一段时间,我们非常接近100%,并且刚刚决定做到最后这一点。但是,我们无法在工程方面证明最后几个百分点的合理性;最后几个百分点纯粹被称为“工艺的骄傲”。我从100%的覆盖率中得到了一个非常小的额外想法,但实际上并不需要它。我们只是通过正常测试获得超过90%的声明覆盖率,这对我们来说是好的。也就是说,我们希望该软件坚如磐石,并且100%的声明覆盖率帮助我们实现了目标。今天获得100%的声明报道也更容易。

即使您不需要100%,测量覆盖率仍然很有用。如果您的测试没有合适的覆盖率,那么应该关注。一个糟糕的测试套件可以有良好的语句覆盖率,但是如果你没有良好的语句覆盖率,那么根据定义,你有一个糟糕的测试套件。您需要多少是权衡:软件的风险(概率和影响)是完全未经测试的?根据定义,它更容易出错(你没有测试它!),但如果你和你的用户可以忍受这些风险(概率和影响),那就没关系。对于许多影响较小的项目,我认为80%-90%的声明覆盖率是可以接受的,并且会更好。

另一方面,如果人们可能因软件中的错误而死亡,那么100%的声明覆盖率是不够的。我至少会添加分支覆盖,或许更多,来检查测试的质量。像DO-178C这样的标准(用于机载系统)采用这种方法 - 如果故障很小,没什么大不了的,但如果故障可能是灾难性的,则需要进行更严格的测试。例如,DO-178C需要MC/DC coverage用于最关键的软件(如果出错则可以快速杀死人的软件)。 MC / DC比声明覆盖甚至分支覆盖更加费力。

答案 14 :(得分:2)

对于使用 testability 编写的新代码,我只有100%的覆盖率。通过适当的封装,每个类和函数都可以进行功能单元测试,同时提供接近100%的覆盖率。这只是添加一些额外的测试,涵盖一些边缘情况,让你达到100%。

你不应该为了获得报道而编写测试。您应该编写测试正确性/合规性的功能测试。通过涵盖所有理由和良好软件设计的良好功能规范,您可以免费获得良好的覆盖范围。

答案 15 :(得分:0)

这里有很多很好的信息,我只是想添加一些我在过去实现100%代码覆盖率时发现的好处

  • 它有助于降低代码复杂度

由于删除一行比编写一个测试用例要容易得多,因此针对100%覆盖率的目标迫使您证明每一行,每个分支,每个if语句的合理性,常常使您发现一种更简单的方法需要更少测试的事情

  • 它有助于开发出良好的测试粒度

您可以通过编写许多小型测试来测试实现的微小部分,从而达到较高的测试覆盖率。这对于棘手的逻辑很有用,但是对于每段代码都如此,无论琐碎多么繁琐,都会使您变慢并成为真正的维护负担,这也使您的代码难以重构。另一方面,要通过非常高级别的端到端行为测试来获得良好的测试覆盖率是非常困难的,因为通常,您要测试的事物涉及许多以复杂方式交互的组件,并且可能情况的排列会非常迅速地变大。因此,如果您很实际,并且还希望实现100%的测试覆盖率,那么您很快就会学会为测试找到粒度级别,通过一些良好的测试就可以达到较高的覆盖率。您可以通过在足够简单的水平上测试组件来实现此目的,以使它们可以合理地涵盖所有极端情况,但又要足够复杂以可以测试有意义的行为。这样的测试最终变得简单,有意义并且对于识别和修复错误很有用。我认为这是一项很好的技能,可以提高代码质量和可维护性。

答案 16 :(得分:0)

不久前,我对coverage in the JUnit implementation进行了一些分析,该分析由Kent Beck和David Saff等人编写和测试。

结论:

  

将线路覆盖应用于世界上测试最好的项目之一,这是我们学到的知识:

     
      
  1. 仔细分析受请求请求影响的代码的覆盖范围比根据阈值监视总体覆盖范围趋势有用。

  2.   
  3. 可以降低不赞成使用的代码的测试标准,但是不要让这影响其余的代码。如果您在连续集成服务器上使用覆盖率阈值,请考虑对已弃用的代码进行不同的设置。

  4.   
  5. 没有理由使用超过2-3条未经测试的代码行的方法。

  6.   
  7. 通常的嫌疑人(简单代码,死代码,恶劣的天气行为……)约占未发现代码的5%。

  8.   
     

总而言之,您应该监视线路覆盖范围吗?并非所有开发团队都这样做,即使在JUnit项目中,这似乎也不是一种标准做法。但是,如果您想要与JUnit开发人员一样出色,那么您的线路覆盖率就不会低于95%。监控覆盖范围是验证这一点的简单第一步。