单元测试的合理代码覆盖率是多少(为什么)?

如果您要强制要求单元测试的最小代码覆盖率,甚至可能作为提交到存储库的要求,它会是什么?

请解释你是如何得出答案的(因为如果你所做的只是选择一个数字,那么我可以自己完成;)

278000 次浏览

85%将是签入标准的良好起点。

我可能选择了各种更高的运输标准——这取决于正在测试的子系统/组件的重要性。

这在很大程度上取决于您的应用程序。例如,一些应用程序主要由无法进行单元测试的GUI代码组成。

阿尔贝托·萨沃亚(Alberto Savoia)的这篇散文准确地回答了这个问题(以一种非常有趣的方式!):

http://www.artima.com/forums/flat.jsp?forum=106&;线程=204677

测试覆盖率

一天早上,一个程序员问 大师:

"我准备写一些单元测试。我应该瞄准什么样的代码覆盖率 “为什么?”

大师回答说:

“不要担心覆盖率,只需编写一些好的测试。

程序员微笑,鞠躬,然后 左

那天晚些时候,第二个程序员 问了同样的问题

大师指着一壶 水开了,说:

“我应该在那个锅里放多少粒米饭?”

程序员看起来很困惑, 回复:

“我怎么可能告诉你?这取决于你需要多少人 食物,他们有多饿,还有什么 你提供的食物,多少米饭 “你有,等等”

”““确切地说,“大师说。

第二个程序员微笑着鞠躬, 向左

一天结束时,第三个 程序员也问了同样的问题 关于代码覆盖率的问题。

“百分之八十,不能少!”大师用严厉的声音回答,

用拳头猛击桌子

第三个程序员微笑着鞠躬, 向左

在这最后的回答之后,一个年轻的 学徒走近伟大 主:

“大师,今天我无意中听到你回答了同样的问题 代码覆盖三种不同 “为什么?”

伟大的大师从他的 主持人:

“过来和我一起喝杯新茶,我们谈谈吧。

在他们的杯子里装满了 抽着热绿茶,伟大的 师父开始回答:

"第一个程序员是新手,刚刚开始测试。 现在他有很多代码,没有 测试。他还有很长的路要走; 此时专注于代码覆盖率 这将是令人沮丧和无用的。 他最好还是习惯 编写和运行一些测试。他可以 “以后再说吧!”

"另一方面,第二个程序员对两者都很有经验 在编程和测试。当我 回答是问她有多少粒 我应该把米饭放在锅里,我 帮助她意识到 测试的必要性取决于一个数字 的因素,她知道这些 比我更好的因素——是她 代码毕竟。没有单一的, 答案很简单而且她很聪明 来处理真相并与之合作 “这……”

“我明白了,”年轻的学徒说, “但如果没有一个简单的 回答,那你为什么回答 第三程序员的百分之八十和 “怎么了?”

大师笑得很厉害 大声说他的肚子,证据表明他 喝的不仅仅是绿茶, 上上下下

"第三个程序员只想要简单的答案-即使有 没有简单的答案…然后没有 “不管怎样,跟着他们。

年轻的学徒和头发花白的人 大师喝完了 茶在沉思的沉默。

如果这是一个完美的世界,100%的代码都会被单元测试覆盖,但是由于这个世界并不完美,所以这是一个你有时间做什么的问题。因此,我建议不要关注某个特定的百分比,而是更多地关注关键领域。如果你的代码写得很好(或者至少是一个合理的传真),那么应该有几个关键点API暴露给其他代码。

把你的测试精力集中在这些API上。确保这些API 1)有充分的文档记录,2)编写的测试用例与留档匹配。如果预期结果与文档不符,那么你的代码、留档或测试用例中都有bug。所有这些都值得审查。

祝你好运!

我不认为会有这样的B/W规则。
应审查代码,特别注意关键细节。
但是,如果它没有经过测试,它有一个bug!

简短回答:60-80%

长答案: 我认为这完全取决于你的项目的性质。我通常通过单元测试每个实际部分来开始一个项目。在项目的第一个“发布”时,你应该根据你正在做的编程类型有一个相当好的基本百分比。在那一点上,你可以开始“强制执行”一个最小的代码覆盖率。

根据代码的关键性,75%-85%是一个很好的经验法则。 运输代码绝对应该比内部公用事业等更彻底地测试。

几天前我们的目标是>80%,但是在我们使用了很多生成的代码之后,我们不在乎%年龄,而是让审阅者打电话询问所需的覆盖率。

这必须取决于您所处的应用程序开发生命周期的哪个阶段。

如果你已经开发了一段时间,并且已经实现了很多代码,并且刚刚意识到你需要考虑代码覆盖率,那么你必须检查你当前的覆盖率(如果它存在),然后使用该基线来设置每个冲刺的里程碑(或在一段时间内冲刺的平均上升),这意味着在继续提供最终用户价值的同时承担代码债务(至少根据我的经验,如果最终用户没有看到新功能,他们不会关心你是否增加了测试覆盖率)。

根据您的领域,拍摄95%并非不合理,但我不得不说,平均而言,您将看到85%到90%的平均情况。

如果您的目标是100%的覆盖率(而不是100%测试所有功能),代码覆盖率是一个误导性指标。

  • 你可以通过一次命中所有行来获得100%。然而,你仍然可能错过测试这些行被击中的特定序列(逻辑路径)。
  • 你不能得到100%,但仍然测试了所有80%/freq使用的代码路径。测试你放入的每个“抛出ExceptionTypeX”或类似的防御性编程保护程序的测试是一种“拥有”而不是“必须拥有”

所以相信你自己或你的开发人员会彻底地覆盖他们代码的每一条路径。务实一点,不要追求神奇的100%覆盖率。如果你TDD你的代码,你应该得到90%以上的覆盖率作为奖励。使用代码覆盖率突出显示你错过的代码块(但是如果你TDD不应该发生…因为你编写代码只是为了通过测试。没有伙伴测试就没有代码。)

我认为正确代码覆盖的最佳症状是单元测试帮助修复的具体问题的数量合理地对应于您创建的单元测试代码的大小。

如果你已经做了相当长的时间的单元测试,我认为没有理由不接近95%以上。然而,至少,我一直在使用80%,即使是测试新手。

这个数字应该只包括项目中编写的代码(不包括框架、插件等),甚至可能排除某些完全由调用外部代码编写的代码组成的类。这种调用应该被模拟/存根。

我有另一个关于测试覆盖率的轶事我想分享。

我们有一个巨大的项目,在推特上,我注意到,700个单元测试,我们只有20%的代码覆盖率

Scott Hanselman回复智慧之言

是正确的20%吗?是正确的20%吗? 代表您的用户的代码 最多?你可以再加50个 #36825;加2%。

再一次,它回到我的关于代码覆盖率答案。你应该在锅里放多少米饭?这取决于。

一般来说,从我读过的几篇关于卓越工程的最佳实践的论文中,单元测试中80%的新代码是产生最佳回报的点。超过CC%,付出的努力会产生更低的缺陷量。这是许多大公司使用的最佳实践。

不幸的是,这些结果大多是公司内部的,所以没有公开的文献可以指出。

查看Crap4j。这是一种比直接代码覆盖率稍微复杂一点的方法。它将代码覆盖率测量与复杂性测量相结合,然后向您展示当前尚未测试的复杂代码。

我对这个难题的回答是对您可以测试的代码有100%的行覆盖率,对您无法测试的代码有0%的行覆盖率。

我目前在Python中的做法是将我的. py模块分为两个文件夹:app1/和app2/,并且在运行单元测试时计算这两个文件夹的覆盖率并直观检查(我必须有一天会自动执行此操作)app1具有100%的覆盖率,app2具有0%的覆盖率。

当/如果我发现这些数字与标准不同时,我会调查并更改代码的设计,以便覆盖范围符合标准。

这确实意味着我可以建议实现库代码的100%行覆盖率。

我偶尔也会查看app2/,看看我是否可以在那里测试任何代码,如果可以,我将它移动到app1/中

现在我不太担心总覆盖率,因为这可能会根据项目的规模而变化很大,但一般来说,我已经看到了70%到90%以上。

使用python,我应该能够设计一个冒烟测试,它可以在测量覆盖率时自动运行我的应用程序,并希望在将冒烟测试与单元测试数据相结合时获得100%的聚合。

代码覆盖率很好,但前提是您从中获得的好处超过实现它的成本/努力。

一段时间以来,我们一直致力于达到80%的标准,但我们刚刚决定放弃这一标准,而是更专注于我们的测试。专注于复杂的业务逻辑等,

做出这个决定是因为我们花了越来越多的时间来追求代码覆盖率和维护现有的单元测试。我们觉得我们已经到了这样一个地步,即我们从代码覆盖率中获得的好处被认为小于我们为实现它所付出的努力。

换个角度看覆盖率:代码写得好,有清晰的控制流是最容易覆盖,最容易阅读,而且通常也是错误最少的代码。在编写代码时考虑清晰性和可覆盖性,并且并行编写单元测试会得到最好的效果。

我认为最重要的是知道随着时间的推移,覆盖面趋势是什么,并理解趋势变化的原因。你认为趋势的变化是好是坏将取决于你对原因的分析。

代码覆盖率很好,但功能覆盖率更好。我不认为要覆盖我写的每一行代码。但我相信,对我想提供的所有功能(即使是我自己带来的、在会议上没有讨论的很酷的功能)编写100%的测试覆盖率。

我不在乎我是否会有测试中没有覆盖的代码,但我会在乎我是否会重构我的代码并最终产生不同的行为。因此,100%的功能覆盖率是我唯一的目标。

我使用cobertura,无论百分比如何,我建议保持cobertura-check任务中的值是最新的。至少,继续将总矿物质和总分支率提高到略低于你当前的覆盖率,但是从未会降低这些值。还要将蚂蚁构建失败属性与此任务绑定。如果由于缺乏覆盖率而构建失败,你知道有人添加了代码但尚未测试它。示例:

<cobertura-check linerate="0"
branchrate="0"
totallinerate="70"
totalbranchrate="90"
failureproperty="build.failed" />

当我认为我的代码没有经过足够的单元测试,并且我不确定下一步要测试什么时,我使用覆盖率来帮助我决定下一步要测试什么。

如果我在单元测试中增加覆盖率-我知道这个单元测试有价值。

这适用于未覆盖、50%覆盖或97%覆盖的代码。

在我看来,答案是“这取决于你有多少时间”。我试着做到100%,但如果我没有用我的时间得到它,我不会大惊小怪。

当我编写单元测试时,我戴的帽子与我开发生产代码时戴的帽子不同。我考虑被测试的代码声称要做什么,以及可能会破坏它的情况是什么。

我通常遵循以下标准或规则:

  1. 单元测试应该是对我的代码的预期行为的一种留档形式,即给定特定输入的预期输出以及客户端可能想要捕获的异常(我的代码的用户应该知道什么?)

  2. 单元测试应该帮助我发现我可能还没有想到的假设条件。(如何使我的代码稳定和健壮?)

如果这两个规则不能产生100%的覆盖率,那就这样吧。但是一旦有时间,我就分析未覆盖的块和行,并确定是否还有没有单元测试的测试用例,或者是否需要重构代码以消除不必要的代码。

特斯特福斯发布我认为答案上下文应该是第二个程序员。

从实践的角度来说,我们需要参数/目标来努力。

我认为这可以在敏捷过程中通过分析代码来“测试”,我们有架构、功能(用户故事),然后得出一个数字。根据我在电信领域的经验,我认为60%是一个很好的检查值。

对于一个设计良好的系统,单元测试从一开始就驱动了开发,我认为85%是一个相当低的数字。设计为可测试的小类应该不难涵盖比这更好的内容。

很容易用这样的方式忽略这个问题:

  • 覆盖的行不等于测试的逻辑,不应该过多地阅读百分比。

没错,但关于代码覆盖率有一些重要的观点需要说明。根据我的经验,如果使用正确,这个指标实际上是非常有用的。话虽如此,我没有见过所有的系统,我敢肯定有很多系统很难看到代码覆盖率分析增加任何真正的价值。代码看起来如此不同,可用测试框架的范围也可能不同。

此外,我的推理主要涉及很短的测试反馈循环。对于我正在开发的产品,最短的反馈循环非常灵活,涵盖了从类测试到进程间信令的所有内容。测试可交付的子产品通常需要5分钟,对于如此短的反馈循环,确实可以使用测试结果(特别是我们在这里看到的代码覆盖率度量)来拒绝或接受存储库中的提交。

使用代码覆盖率指标时,您不应该只有一个必须满足的固定(任意)百分比。在我看来,这样做并没有给你代码覆盖率分析的真正好处。相反,定义以下指标:

  • 低水位线(LWM),是测试系统中最低的未覆盖线数
  • 高水印(HWM),被测系统有史以来最高的代码覆盖率

只有当我们不高于LWM且不低于HWM时,才能添加新代码。换句话说,代码覆盖率是不允许减少,应该覆盖新代码。注意我怎么说应该和不必须(下面解释)。

但这是否意味着你将不可能清理那些经过良好测试、不再有用的旧垃圾?是的,这就是为什么你必须在这些事情上务实。在某些情况下,规则必须被打破,但对于你典型的日常集成,我的经验是,这些指标非常有用。它们给出了以下两个含义。

  • 提升可测试代码。 添加新代码时,您必须努力使代码可测试,因为您必须尝试用测试用例覆盖所有内容。可测试代码通常是一件好事。

  • 遗留代码的测试覆盖率随着时间的推移而增加。 当添加新代码并且无法用测试用例覆盖时,可以尝试覆盖一些遗留代码来绕过LWM规则。这种有时必要的作弊至少给出了一个积极的副作用,即遗留代码的覆盖率会随着时间的推移而增加,使得看似严格的这些规则的执行在实践中相当实际。

同样,如果反馈循环太长,在集成过程中设置这样的东西可能完全不切实际。

我还想提到代码覆盖率指标的两个更普遍的好处。

消极的,为了完整性。

  • 在一个有很多开发人员参与的大项目中,并不是每个人都一定是测试天才。有些人倾向于使用代码覆盖率度量作为代码被测试的证明,这与事实相去甚远,正如这个问题的许多其他答案中提到的。如果使用得当,这是一个可以给你带来一些很好好处的指标,但如果它被滥用,实际上会导致糟糕的测试。除了上面提到的非常有价值的副作用之外,覆盖线只表明被测系统在一些输入数据上可以达到该行,并且它可以执行而不会挂起或崩溃。

代码覆盖率只是另一个指标。就其本身而言,它可能非常具有误导性(参见www.thoughtworks.com/insights/blog/are-test-coverage-metrics-overrated)。因此,你的目标不应该是实现100%的代码覆盖率,而是确保测试应用程序的所有相关场景。

我最喜欢的代码覆盖率是100%带有星号。使用星号是因为我更喜欢使用允许我将某些行标记为“不计数”行的工具。如果我已经覆盖了100%的“计数”行,我就完成了。

基本过程是:

  1. 我编写测试来练习我能想到的所有功能和边缘情况(通常从留档工作)。
  2. 我运行代码覆盖工具
  3. 我检查没有覆盖的任何行或路径,以及我认为不重要或无法到达的任何行或路径(由于防御性编程),我标记为不计数
  4. 我编写新的测试来覆盖缺失的行,如果没有提到这些边缘情况,则改进留档。

通过这种方式,如果我和我的协作者将来添加新代码或更改测试,如果我们错过了重要的内容,可以使用一条亮线来告诉我们覆盖率是否下降到100%以下。然而,它也提供了处理不同测试优先级的灵活性。

Jon Limjap提出了一个很好的观点——没有一个数字可以作为每个项目的标准。有些项目就是不需要这样的标准。在我看来,公认的答案不足之处在于描述一个人如何为给定的项目做出决定。

我将尝试这样做。我不是测试工程方面的专家,很乐意看到一个更明智的答案。

何时设置代码覆盖率要求

首先,为什么要强加这样一个标准?一般来说,当你想在你的过程中引入经验置信度时。我说的“经验置信度”是什么意思?真正的目标正确性。对于大多数软件,我们不可能在所有输入中都知道这一点,所以我们可以说代码是经过良好测试。这更容易理解,但仍然是一个主观标准:你是否达到了它,总是会有争论。这些辩论是有用的,应该发生,但它们也暴露了不确定性。

代码覆盖率是一个客观的衡量指标:当你看到代码覆盖率报告时,对于是否达到标准是否有用没有任何歧义。它能证明正确性吗?完全不能,但它与代码的测试程度有明显的关系,而这反过来又是我们增加对其正确性信心的最佳方式。代码覆盖率是我们关心的不可估量质量的可衡量近似值。代码覆盖率是我们关注的不可估量质量的可衡量近似值。

具有经验标准可以增加价值的一些具体情况:

  • 满足利益相关者。对于许多项目来说,有各种对软件质量感兴趣的参与者,他们可能不参与软件的日常开发(经理、技术负责人等)说“我们将编写我们真正需要的所有测试”并不令人信服:他们要么需要完全信任,要么通过持续的密切监督进行验证(假设他们甚至有这样做的技术理解。)提供可衡量的标准并解释它们如何合理地接近实际目标更好。
  • 抛开EYZ0利益相关者不谈,如果你的团队有多个人写代码写测试,那么“测试良好”的定义是有歧义的。你的同事对什么级别的测试足够好的想法是一致的吗?可能不是。你如何调和这一点?找到一个你们都同意的指标,并接受它为一个合理的近似值。这在大团队中特别有用,例如,在大团队中,主管可能没有直接监督初级开发。信任网络也很重要,但如果没有客观的测量,即使每个人都真诚地行事,团队行为也很容易变得不一致。
  • 让自己保持诚实。即使你是项目中唯一的开发人员和利益相关者,你也可能对软件有某些品质。你可以使用代码覆盖率作为合理的近似值,让机器为你测量,而不是对软件的测试程度进行持续的主观评估(这需要工作)。

使用哪些指标

代码覆盖率不是一个单一的度量标准;有几种不同的方法来衡量覆盖率。您可以根据哪一种设置标准取决于您使用该标准来满足什么。

我将使用两个常见的指标作为示例,说明何时可以使用它们来设置标准:

  • 语句覆盖:测试期间执行了多少语句?有助于了解代码的物理覆盖:我编写的代码中有多少已经实际测试?
    • 这种覆盖支持较弱的正确性参数,但也更容易实现。如果你只是使用代码覆盖来确保的东西得到测试(而不是作为测试质量的指标),那么语句覆盖可能就足够了。
  • 分支覆盖率:当存在分支逻辑(例如if)时,是否已经评估了两个分支?这可以更好地了解你的代码的逻辑覆盖:我测试了我的代码可能采用的多少条可能路径?
    • 这种覆盖率是一个更好的指标,表明程序已经在一组全面的输入中进行了测试。如果您使用代码覆盖率作为对正确性信心的最佳经验近似,您应该根据分支覆盖率或类似的标准来设置标准。

还有许多其他度量(行覆盖率类似于语句覆盖率,但是对于多行语句产生不同的数值结果,例如;条件覆盖率和路径覆盖率类似于分支覆盖率,但是反映了您可能遇到的程序执行的可能排列的更详细视图。)

需要多少百分比

最后,回到最初的问题:如果你设置代码覆盖率标准,这个数字应该是多少?

希望在这一点上很清楚,我们首先谈论的是近似值,所以我们选择的任何数字都将是固有的近似值。

可以选择的一些数字:

  • 百分之百。你可能选择这个是因为你想确保一切都经过测试。这并不能让你深入了解测试质量,但确实告诉你某些质量的某些测试已经触及了每个语句(或分支等)。同样,这回到了置信度:如果你的覆盖率低于100%,那么你知道代码的某些子集未经测试。
    • 有些人可能会说这很愚蠢,你应该只测试代码中真正重要的部分。我认为你也应该只维护代码中真正重要的部分。删除未经测试的代码也可以提高代码覆盖率。
  • 99%(或95%,其他90年代的数字)适用于您希望传达一定程度的信心类似到100%的情况,但要给自己留出一些空间,以免担心偶尔出现难以测试的代码角落。
  • 80%。这个数字我用过好几次了,但是并不完全知道它是从哪里来的。我认为认为可能是对80-20规则的一种奇怪的盗用;一般来说,这里的目的是表明你的代码大多数已经测试过了。(是的,51%也可以是“最多”,但是80%更能反映大多数人平均的意思。)这适用于中等水平的情况,其中“测试良好”不是高优先级(你不想在低价值的测试上浪费精力),但已经足够高了,你仍然希望有一些标准到位。

在实践中,我没有见过低于80%的数字,很难想象会有一个人设定这些标准。这些标准的作用是增加对正确性的信心,低于80%的数字并不是特别鼓舞信心。(是的,这是主观的,但同样,我们的想法是在设定标准时做出一次主观选择,然后使用客观的衡量标准。)

其他备注

上面假设正确性是目标。代码覆盖率只是信息;它可能与其他目标相关。例如,如果你关心可运维性,你可能关心松耦合,这可以通过可测试性来证明,而可测试性又可以通过代码覆盖率来衡量(以某种方式)。所以你的代码覆盖率标准也为近似“可运维性”的质量提供了经验基础。

我更喜欢做BDD,它使用自动化验收测试、可能的其他集成测试和单元测试的组合。对我来说,问题是整个自动化测试套件的目标覆盖范围应该是什么。

除此之外,答案取决于你的方法论、语言、测试和覆盖工具。在Ruby或Python中做TDD时,保持100%的覆盖率并不难,而且非常值得这样做。管理100%的覆盖率比管理90%左右的覆盖率要容易得多。也就是说,在出现覆盖差距时填补覆盖差距要容易得多(当做TDD时,良好的覆盖差距很少,通常值得你花时间),而不是管理一系列你没有得到的覆盖差距,并且由于你不断的未覆盖代码背景而错过覆盖回归。

答案也取决于你的项目的历史。我只发现上述方法在从一开始就以这种方式管理的项目中是实用的。我已经大大提高了对大型遗留项目的覆盖,这样做是值得的,但我从未发现回头填补每一个覆盖空白是实用的,因为未经测试的旧代码还不够好理解,无法正确和快速地完成。

许多商店不重视测试,所以如果你高于零,至少会有一些价值增值——所以可以说非零并不坏,因为许多人仍然是零。

在. Net的世界里,人们经常引用80%是合理的,但是他们在解决方案层面这么说。我更喜欢在项目层面来衡量:UI项目如果有Selenium等或者人工测试,30%可能没问题;数据层项目,20%可能没问题,但是对于业务规则层来说,95%+可能是可以实现的,如果不是完全必要的话。所以整体覆盖率可能是,比如说60%,但是关键业务逻辑可能要高得多。

我也听说过:渴望100%,你会达到80%;但渴望80%,你会达到40%。

底线:应用80:20规则,让你的应用程序的bug计数指导你。