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

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

有帮助吗?

解决方案

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

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

Testivus 关于测试覆盖率

一个清晨,一个程序员问伟大的大师:

“我准备编写一些单元测试。我应该为什么代码覆盖?”

伟大的大师回答说:

“不用担心覆盖范围,只需编写一些好的测试即可。”

程序员笑了笑,鞠躬,离开了。

...

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

伟大的大师指着一锅沸水说:

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

看上去很困惑的程序员回答:

“我怎么可能告诉你呢?这取决于您需要喂多少人,他们有多饥饿,您所提供的其他食物,可用的大米等等。”

“正是如此。”大师说道。

第二个程序员笑了笑,鞠躬,向左。

...

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

“百分之八十,也不少!”主人用严厉的声音回答,将拳头砸在桌子上。

第三名程序员笑了笑,鞠躬,向左。

...

在最后回答之后,一位年轻的学徒接近了伟大的大师:

“伟大的大师,今天我听到您回答了有关代码覆盖的同样问题,并带有三个不同的答案。为什么?”

伟大的大师从椅子上站起来:

“跟我去喝点新鲜茶,我们来谈谈。”

在他们用吸烟绿茶的杯子里填满杯子后,伟大的大师开始回答:

“第一位程序员是新人,刚刚开始测试。现在他有很多代码,没有测试。他还有很长的路要走;目前关注代码覆盖范围将令人沮丧,而且毫无用处。他最好只是习惯写作和进行一些测试。他以后会担心覆盖范围。”

“另一方面,第二个程序员在编程和测试方面都具有相当的经验。当我问她应该放入锅中多少米的谷物时,我帮助她意识到必要的测试量取决于许多因素,她比我更了解这些因素 - 毕竟是她的代码。没有一个简单的答案,她足够聪明,可以处理真相并与之合作。”

“我明白了,”年轻的学徒说,“但是,如果没有一个简单的答案,那么为什么您要回答第三个程序员'80%,而同样更少'?”

这位伟大的大师笑得如此大声,以至于他的肚子,证据表明他喝的不仅仅是绿茶,而是上下失败。

“第三个程序员只需要简单的答案 - 即使没有简单的答案……然后无论如何都不会跟随它们。”

年轻的学徒和灰熊大师在沉思的沉默中结束了他们的茶。

其他提示

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

  • 击中所有线一次即可获得 100%。但是,您仍然可能会错过测试命中这些行的特定序列(逻辑路径)。
  • 您无法获得 100%,但仍然测试了所有 80%/freq 使用的代码路径。对您放入的每个“抛出 ExceptionTypeX”或类似的防御性编程防护进行测试是“最好有”而不是“必须有”

因此,请相信您自己或您的开发人员能够彻底覆盖其代码中的每一条路径。务实一点,不要追求神奇的 100% 覆盖率。如果您对代码进行 TDD,那么您应该获得 90% 以上的覆盖率作为奖励。使用代码覆盖来突出显示您错过的代码块(如果您采用 TDD,则不应发生这种情况..因为您编写代码只是为了通过测试。如果没有其合作伙伴测试,任何代码都不可能存在。)

代码覆盖率很高,但功能覆盖率甚至更好。我不相信要涵盖我写的每一行。但我确实相信对我想要提供的所有功能编写 100% 的测试覆盖率(即使是我自己带来的、在会议期间没有讨论的额外很酷的功能)。

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

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

我会尝试这样做。我不是测试工程方面的专家,很高兴看到更明智的答案。

何时设置代码覆盖率要求

首先,你为什么要强加这样一个标准?一般来说,当您想在流程中引入经验信心时。“经验信心”是什么意思?嗯,真正的目标 正确性. 。对于大多数软件,我们不可能在所有输入中都知道这一点,因此我们只能说代码是 经过充分测试. 。这是更容易理解的,但仍然是一个主观标准:无论您是否遇到过它,都将始终存在争议。这些辩论是有用的并且应该进行,但它们也暴露了不确定性。

代码覆盖率 是一个客观的测量:一旦您看到覆盖率报告,就可以清楚地知道满足的标准是否有用。能证明其正确性吗?完全不是,但它与代码的测试程度有明显的关系,这反过来又是我们增强对其正确性信心的最佳方法。代码覆盖率是我们关心的不可衡量的质量的可衡量的近似值。

在一些具体情况下,拥有经验标准可以增加价值:

  • 让利益相关者满意。 对于许多项目,有各种对软件质量感兴趣的参与者,他们可能不参与软件的日常开发(经理、技术主管等),他们说“我们将编写所有我们真正需要的测试”并不令人信服:他们要么需要完全信任,要么需要通过持续的密切监督进行验证(假设他们甚至具备这样做的技术理解)。提供可衡量的标准并解释它们如何合理地接近实际目标会更好。
  • 使团队行为正常化。 除了利益相关者之外,如果您正在一个团队工作的团队,其中有多个人正在编写代码和测试,那么有资格“经过良好测试”的含义是歧义的。所有同事都对哪种测试水平足够好都有相同的想法吗?可能不会。您如何协调这一点?找到一个大家都同意的指标,并接受它作为合理的近似值。这在大型团队中尤其(但并非排他)有用,例如,领导者可能无法直接监督初级开发人员。信任网络也很重要,但如果没有客观的衡量标准,即使每个人都真诚行事,群体行为也很容易变得不一致。
  • 为了让自己保持诚实。 即使您是项目的唯一开发人员和唯一利益相关者,您也可能会考虑到该软件的某些品质。您可以使用代码覆盖率作为合理的近似值,然后让机器为您测量,而不是对软件的测试情况进行持续的主观评估(这需要工作)。

使用哪些指标

代码覆盖率不是一个单一的指标;有几种不同的测量覆盖率的方法。您可以设定哪一个标准取决于您使用该标准来满足什么要求。

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

  • 报表覆盖范围: :测试期间执行了多少百分比的语句?有助于了解 物理覆盖 你的代码:我编写的代码中有多少是我实际测试过的?
    • 这种覆盖支持较弱的正确性论证,但也更容易实现。如果您只是使用代码覆盖率来确保 事物经过测试(而不是作为测试质量的指标),那么语句覆盖率可能就足够了。
  • 分支机构覆盖范围: :当存在分支逻辑时(例如一个 if),两个分支都经过评估了吗?这可以让我们更好地了解 逻辑覆盖 你的代码:我测试过我的代码可能采用的路径有多少?
    • 这种覆盖范围是一个更好的指标,表明程序已经通过一组全面的输入进行了测试。如果您使用代码覆盖率作为正确性置信度的最佳经验近似值,则应根据分支覆盖率或类似内容设置标准。

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

需要多少百分比

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

希望此时我们已经清楚地知道我们首先讨论的是近似值,因此我们选择的任何数字本质上都是近似值。

人们可以选择的一些数字:

  • 100%. 。您可能会选择此选项,因为您希望确保所有内容都经过测试。这并不能让您深入了解测试质量,但确实告诉您某种质量的某些测试已经触及每个语句(或分支等)。同样,这又回到了置信度:如果您的覆盖率低于 100%,您 知道 您的代码的某些子集未经测试。
    • 有些人可能会认为这很愚蠢,您应该只测试代码中真正重要的部分。我认为您也应该只维护代码中真正重要的部分。还可以通过删除未经测试的代码来提高代码覆盖率。
  • 99% (或 95%,其他数字在九十多岁。)适用于您想要表达一定程度的信心的情况 相似的 到 100%,但给自己留一些余量,不用担心偶尔难以测试的代码角落。
  • 80%. 。我见过这个号码使用过几次,但不完全知道它的来源。我 思考 这可能是对 80-20 规则的奇怪挪用;一般来说,这里的目的是表明 最多 您的代码已经过测试。(是的,51% 也是“大多数”,但 80% 更能反映大多数人的情况 意思是 大多数情况下。)这适用于中间立场的情况,其中“经过充分测试”不是高优先级(您不想在低价值测试上浪费精力),但已经足够优先,您仍然会这样做喜欢制定一些标准。

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

其他注意事项

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

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

底层流程是:

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

这样,如果我和我的合作者将来添加新代码或更改测试,就会有一条明线告诉我们是否错过了一些重要的内容 - 覆盖率降至 100% 以下。然而,它还提供了处理不同测试优先级的灵活性。

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

我们有一个巨大的项目,我在推特上指出, 通过 700 个单元测试,我们只有 20% 的代码覆盖率.

斯科特·汉塞尔曼 回复了 至理名言:

这是正确的 20% 吗?是代表用户击中最多代码的20%吗?您可以添加50个测试,只添加2%。

再次,它又回到了我的 关于代码覆盖率的测试 回答。锅里应该放多少米?这取决于。

如果这是一个完美的世界,单元测试将覆盖 100% 的代码。然而,由于这不是一个完美的世界,所以这取决于你有时间做什么。因此,我建议减少对特定百分比的关注,而更多地关注关键领域。如果您的代码写得很好(或者至少是其合理的复制品),那么应该有几个关键点可以将 API 暴露给其他代码。

将您的测试工作集中在这些 API 上。确保 API 1) 有详细记录,2) 编写的测试用例与文档相匹配。如果预期结果与文档不匹配,则说明您的代码、文档或测试用例中存在错误。所有这些都值得审查。

祝你好运!

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

很容易通过以下内容来驳回这个问题:

  • 被覆盖的线并不等于经过测试的逻辑,人们不应该过多地解读百分比。

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

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

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

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

仅当我们不高于 LWM 且不低于 HWM 时,才能添加新代码。换句话说,代码覆盖率是 不允许减少, ,并且应覆盖新代码。请注意我如何说“应该”和“不是必须”(如下所述)。

但这是否意味着您将无法清除那些经过充分测试、不再使用的旧垃圾?是的,这就是为什么你必须对这些事情采取务实的态度。在某些情况下,必须打破规则,但对于典型的日常集成,我的经验是这些指标非常有用。他们给出了以下两个含义。

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

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

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

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

  • 代码覆盖率分析是动态代码分析的一部分(与静态代码分析相反,即皮棉)。动态代码分析过程中发现的问题(通过purify系列等工具, http://www-03.ibm.com/software/products/en/rational-purify-family)是未初始化的内存读取(UMR)、内存泄漏等。 只有当代码被执行的测试用例覆盖时才能发现这些问题. 。测试用例中最难覆盖的代码通常是系统中的异常情况,但如果您希望系统优雅地失败(即错误跟踪而不是崩溃)您可能还想花一些精力来覆盖动态代码分析中的异常情况。只要运气不好,UMR 就可能导致段错误或更严重的情况。

  • 人们为保持 100% 新代码而感到自豪,并且人们以与其他实现问题类似的热情讨论测试问题。如何以更可测试的方式编写这个函数?您将如何尝试报道这种异常情况等?

为了完整起见,还有一个否定。

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

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

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

许多商店不重视测试,所以如果你的值高于零,至少会有一些价值升值 - 所以可以说非零也不错,因为许多仍然为零。

在 .Net 世界中,人们经常引用 80% 的说法是合理的。但他们是在解决方案层面这么说的。我更喜欢在项目级别进行衡量:如果您有 Selenium 等或手动测试,对于 UI 项目来说 30% 可能就可以了,对于数据层项目来说 20% 可能就可以了,但是对于业务规则层来说 95%+ 可能是可以实现的,如果不是完全必要的话。因此,总体覆盖率可能是 60%,但关键业务逻辑可能要高得多。

我也听说过这个:立志达到 100%,你就会达到 80%;但立志达到 80%,你就会达到 40%。

底线:应用 80:20 规则,让应用程序的错误计数来指导您。

我使用 cobertura,无论百分比如何,我都建议保持 cobertura-check 任务中的值是最新的。至少,继续将totallinerate和totalbranchrate提高到略低于当前的覆盖范围,但是 绝不 降低这些值。还将 Ant 构建失败属性与此任务联系起来。如果构建由于缺乏覆盖而失败,则您知道某人添加了代码但尚未对其进行测试。例子:

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

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

如果我增加单元测试的覆盖范围 - 我知道这个单元测试有价值。

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

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

如果您已经进行单元测试相当长的时间,我认为没有理由不接近 95%+。然而,至少,我总是使用 80% 的工作,即使是刚开始测试时也是如此。

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

一般来说,从我读过的几篇工程卓越最佳实践论文来看,单元测试中新代码的 80% 是产生最佳回报的点。如果高于该 CC%,则相对于付出的努力而言,会产生更少的缺陷。这是许多大公司都采用的最佳实践。

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

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

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

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

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

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

答案还取决于您的项目的历史。我只发现上述内容对于从一开始就以这种方式管理的项目来说是实用的。我极大地提高了大型遗留项目的覆盖范围,并且这样做是值得的,但我从未发现返回并填补每个覆盖范围空白是可行的,因为旧的未经测试的代码还没有被充分理解,无法正确执行此操作,并且迅速地。

查看 废话4j. 。这是一种比直接代码覆盖稍微复杂一些的方法。它将代码覆盖率测量与复杂性测量相结合,然后向您显示当前未测试的复杂代码。

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

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

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

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

我偶尔也会查看 app2/ 以查看是否可以测试其中的任何代码,如果可以,我会将其移至 app1/

现在我不太担心总体覆盖率,因为根据项目规模的不同,总体覆盖率可能会有很大差异,但一般来说,我看到的覆盖率是 70% 到 90% 以上。

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

从另一个角度看报道:编写良好、控制流程清晰的代码是最容易覆盖、最容易阅读的,而且通常是错误最少的代码。恕我直言,通过在编写代码时考虑到清晰性和可覆盖性,并通过与代码并行编写单元测试,您可以获得最佳结果。

在我看来,答案是“这取决于你有多少时间”。我努力达到100%,但如果我在有限的时间内没有达到目标,我也不会大惊小怪。

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

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

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

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

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

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

我认为不可能有这样的黑白规则。
应审查代码,特别注意关键细节。
但是,如果没有经过测试,它就有一个错误!

简短回答:60-80%

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

根据代码的重要性,75%-85% 之间的任何地方都是一个很好的经验法则。运输代码绝对应该比内部公用设施等进行更彻底的测试。

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

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

根据您的领域,达到 95% 并不是没有道理的,但我不得不说,平均而言,您将看到 85% 到 90% 的平均情况。

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

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

几天前我们的目标是 >80%,但是在我们使用了大量生成的代码之后,我们不关心 %age,而是让审阅者就所需的覆盖率进行电话咨询。

从 Testivus 的帖子来看,我认为答案上下文应该是第二个程序员。从实际角度来看,我们需要努力实现的参数/目标。我认为这可以在敏捷过程中通过分析我们拥有的架构、功能(用户故事)的代码来“测试”,然后得出一个数字。根据我在电信领域的经验,我认为 60% 是一个值得检查的值。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top