如果您要为单元测试规定最低百分比的代码覆盖率,甚至可能作为提交到存储库的要求,那会是什么?
请解释您是如何得出答案的(因为如果您所做的只是选择一个数字,那么我可以自己完成所有这些;)
如果您要为单元测试规定最低百分比的代码覆盖率,甚至可能作为提交到存储库的要求,那会是什么?
请解释您是如何得出答案的(因为如果您所做的只是选择一个数字,那么我可以自己完成所有这些;)
Alberto Savoia 的这篇散文准确地回答了这个问题(以一种非常有趣的方式!):
http://www.artima.com/forums/flat.jsp?forum=106&thread=204677
Testivus 测试覆盖率
一天一大早,一位程序员问大神:
“我准备写一些单元测试。我应该以什么样的代码覆盖率为目标?”</p>
大神回复:
“不要担心覆盖率,只需编写一些好的测试即可。”</p>
程序员笑了笑,鞠了一躬,离开了。
...
那天晚些时候,第二个程序员问了同样的问题。
大师指着一壶开水说道:
“我应该在那个锅里放几粒米?”</p>
程序员一脸疑惑,答道:
“我怎么可能告诉你?这取决于您需要喂食多少人,他们有多饿,您提供的其他食物是什么,您有多少大米,等等。”</p>
“正是,”伟大的大师说。
第二个程序员笑了笑,鞠了一躬,然后离开了。
...
一天快结束的时候,第三位程序员过来问了同样的关于代码覆盖率的问题。
“百分之八十,不少于!” 师父厉声应道,一拳砸在桌子上。
第三个程序员笑了笑,鞠了一躬,然后离开了。
...
最后一个回答后,一位年轻的徒弟走到大师面前:
“伟大的大师,今天我无意中听到您用三个不同的答案回答了关于代码覆盖率的同一个问题。为什么?”</p>
大师从椅子上站了起来:
“来和我喝点新鲜的茶,我们谈谈。”</p>
将茶杯倒满冒着热气的绿茶后,大师开始回答:
“第一个程序员是新来的,刚刚开始测试。现在他有很多代码,没有测试。他还有很长的路要走;此时专注于代码覆盖率会令人沮丧且毫无用处。他最好习惯于编写和运行一些测试。他可以担心以后的报道。”</p>
“另一方面,第二位程序员在编程和测试方面都非常有经验。当我问她我应该在锅里放多少米粒时,我帮助她意识到必要的测试量取决于许多因素,她比我更了解这些因素——毕竟这是她的代码. 没有单一的、简单的答案,而且她足够聪明,能够处理事实并与之合作。”</p>
“我明白了,”年轻学徒说,“但如果没有一个简单的答案,那你为什么要回答第三个程序员‘百分之八十而且不少于’?”</p>
伟大的大师笑得如此大声,以至于他的肚子,证明他喝的不仅仅是绿茶,上下翻腾。
“第三个程序员只想要简单的答案——即使没有简单的答案……然后无论如何都不会遵循它们。”</p>
年轻的徒弟和头发花白的大师喝完了茶,陷入了沉思。
如果您的目标是 100% 覆盖率(而不是 100% 测试所有功能),那么代码覆盖率是一个误导性指标。
因此,请相信您自己或您的开发人员会彻底了解并涵盖其代码的每一条路径。务实,不要追逐神奇的 100% 覆盖率。如果您对代码进行 TDD,您应该获得 90% 以上的覆盖率作为奖励。使用代码覆盖来突出显示您错过的代码块(如果您使用 TDD,则不会发生这种情况。因为您编写代码只是为了通过测试。没有它的伙伴测试,任何代码都无法存在。)
Jon Limjap 提出了一个很好的观点——没有一个数字可以作为每个项目的标准。有些项目不需要这样的标准。在我看来,公认的答案不足之处在于描述人们如何为给定项目做出该决定。
我会尝试这样做。我不是测试工程方面的专家,很乐意看到更明智的答案。
首先,您为什么首先要强加这样的标准?一般来说,当您想在您的过程中引入经验信心时。我所说的“经验信心”是什么意思?好吧,真正的目标正确性。对于大多数软件,我们不可能在所有输入中都知道这一点,所以我们只能说代码是经过良好测试的。这是比较可知的,但仍然是一个主观标准:无论你是否满足它,它总是值得商榷的。这些辩论是有用的,应该发生,但它们也暴露了不确定性。
代码覆盖率是一个客观的衡量标准:一旦你看到你的覆盖率报告,就可以毫不含糊地知道是否符合标准是有用的。它证明了正确性吗?一点也不,但它与代码的测试良好程度有着明确的关系,这反过来又是我们提高对其正确性信心的最佳方式。代码覆盖率是我们关心的不可衡量的质量的可衡量的近似值。
一些具有经验标准可以增加价值的具体案例:
代码覆盖率不是一个单一的指标;有几种不同的测量覆盖率的方法。您可以根据哪个标准来设定标准取决于您使用该标准来满足什么。
我将使用两个常见指标作为示例,说明何时可以使用它们来设置标准:
if
)时,是否评估了两个分支?这可以更好地了解代码的逻辑覆盖率:我测试了我的代码可能采用的多少条路径?
还有许多其他指标(行覆盖率类似于语句覆盖率,但对于多行语句会产生不同的数值结果,例如;条件覆盖率和路径覆盖率类似于分支覆盖率,但反映了对可能排列的更详细的视图您可能会遇到的程序执行。)
最后,回到最初的问题:如果设置代码覆盖率标准,这个数字应该是多少?
希望在这一点上很清楚,我们谈论的是一个近似值,所以我们选择的任何数字本质上都是近似值。
人们可能会选择的一些数字:
在实践中,我还没有看到低于 80% 的数字,并且很难想象有人会设置它们的情况。这些标准的作用是增加对正确性的信心,低于 80% 的数字并不是特别能激发信心。(是的,这是主观的,但同样,我们的想法是在设定标准时做出一次主观选择,然后使用客观的测量方法。)
以上假设正确性是目标。代码覆盖率只是信息;它可能与其他目标有关。例如,如果您关心可维护性,您可能关心松散耦合,这可以通过可测试性来证明,而可测试性又可以(以某些方式)通过代码覆盖率来衡量。因此,您的代码覆盖率标准也为近似“可维护性”的质量提供了经验基础。
代码覆盖率很好,但功能覆盖率更好。我不相信涵盖我写的每一行。但我确实相信编写我想要提供的所有功能的 100% 测试覆盖率(即使是我自己带来的额外酷炫的功能,并且在会议期间没有讨论过)。
我不在乎我是否会有测试中未涵盖的代码,但我会关心我是否会重构我的代码并最终产生不同的行为。因此,100% 的功能覆盖率是我唯一的目标。
我最喜欢的代码覆盖率是 100%,带有星号。出现星号是因为我更喜欢使用允许我将某些行标记为“不计入”行的工具。如果我已经覆盖了 100% 的“计数”行,我就完成了。
底层流程是:
这样,如果我和我的合作者将来添加新代码或更改测试,就会有一条亮线告诉我们是否遗漏了一些重要的东西——覆盖率下降到 100% 以下。然而,它也提供了处理不同测试优先级的灵活性。
我想分享另一个关于测试覆盖率的轶事。
我们有一个庞大的项目,在 twitter 上,我注意到,通过 700 个单元测试,我们只有 20% 的代码覆盖率。
是正确的 20% 吗?是 20% 代表您的用户最常使用的代码吗?您可能会再添加 50 个测试,但仅添加 2%。
再次,它可以追溯到我的Testivus on Code Coverage Answer。你应该在锅里放多少米?这取决于。
许多商店不重视测试,所以如果你的分数高于零,至少会有一些价值升值 - 所以可以说非零并不坏,因为很多仍然是零。
在 .Net 世界中,人们经常引用 80% 的合理性。但他们在解决方案层面这么说。我更喜欢在项目级别进行测量:如果您有 Selenium 等或手动测试,UI 项目可能会达到 30%,数据层项目可能会达到 20%,但对于业务来说,95% 以上可能是可以实现的规则层,如果不是完全必要的话。所以总体覆盖率可能是 60%,但关键业务逻辑可能要高得多。
我也听说过:渴望100%,你就会达到80%;但渴望达到 80%,你就会达到 40%。
底线:应用 80:20 规则,让您的应用程序的错误计数指导您。
对于一个设计良好的系统,单元测试从一开始就推动了开发,我会说 85% 是一个相当低的数字。设计为可测试的小类不应该比这更难覆盖。
很容易通过以下方式忽略这个问题:
没错,但是关于代码覆盖率有一些重要的地方需要说明。根据我的经验,如果使用得当,这个指标实际上非常有用。话虽如此,我还没有看到所有的系统,而且我敢肯定有很多系统很难看到代码覆盖率分析增加了任何真正的价值。代码看起来如此不同,可用测试框架的范围也可能不同。
此外,我的推理主要涉及相当短的测试反馈循环。对于我正在开发的产品,最短的反馈循环非常灵活,涵盖了从类测试到进程间信号的所有内容。测试可交付的子产品通常需要 5 分钟,对于如此短的反馈循环,确实可以使用测试结果(特别是我们在此处查看的代码覆盖率指标)来拒绝或接受存储库中的提交。
使用代码覆盖率指标时,您不应该只是有一个必须满足的固定(任意)百分比。在我看来,这样做并没有给你带来代码覆盖分析的真正好处。相反,定义以下指标:
只有在我们不超过 LWM 并且我们不低于 HWM 的情况下才能添加新代码。换句话说,代码覆盖率不允许减少,应该覆盖新代码。注意我怎么说应该和不必须(解释如下)。
但这是否意味着您将无法清除已不再使用的经过良好测试的旧垃圾?是的,这就是为什么你必须对这些事情务实。在某些情况下必须打破规则,但对于您典型的日常集成,我的经验是这些指标非常有用。他们给出了以下两个含义。
可测试的代码得到提升。添加新代码时,您确实必须努力使代码可测试,因为您必须尝试用测试用例覆盖所有代码。可测试的代码通常是一件好事。
遗留代码的测试覆盖率随着时间的推移而增加。当添加新代码并且无法用测试用例覆盖它时,可以尝试覆盖一些遗留代码来绕过 LWM 规则。这种有时必要的作弊至少会产生积极的副作用,即遗留代码的覆盖率会随着时间的推移而增加,使得看似严格执行这些规则在实践中非常务实。
再说一次,如果反馈循环太长,在集成过程中设置这样的东西可能完全不切实际。
我还想提一下代码覆盖率指标的两个更普遍的好处。
代码覆盖分析是动态代码分析的一部分(与静态代码分析相反,即 Lint)。在动态代码分析期间发现的问题(通过 purify 系列,http ://www-03.ibm.com/software/products/en/rational-purify-family 等工具)是诸如未初始化内存读取(UMR)之类的问题,内存泄漏等。这些问题只有在代码被执行的测试用例覆盖时才能被发现。测试用例中最难覆盖的代码通常是系统中的异常用例,但是如果您希望系统优雅地失败(即错误跟踪而不是崩溃),您可能需要付出一些努力来覆盖异常用例在动态代码分析中也是如此。如果运气不好,UMR 可能会导致段错误或更糟。
人们为保持 100% 的新代码而感到自豪,人们以与其他实现问题类似的热情讨论测试问题。如何以更可测试的方式编写此函数?您将如何尝试涵盖这种异常情况等。
为了完整性,否定。
如果这是一个完美的世界,单元测试将覆盖 100% 的代码。然而,由于这不是一个完美的世界,这取决于你有时间做什么。因此,我建议少关注特定百分比,多关注关键领域。如果你的代码写得很好(或者至少是一个合理的复制品),那么应该有几个关键点将 API 暴露给其他代码。
将您的测试工作集中在这些 API 上。确保 API 1) 有良好的文档记录和 2) 编写了与文档匹配的测试用例。如果预期结果与文档不匹配,那么您的代码、文档或测试用例中存在错误。所有这些都是很好的审查。
祝你好运!
代码覆盖率只是另一个指标。就其本身而言,它可能非常具有误导性(请参阅www.thoughtworks.com/insights/blog/are-test-coverage-metrics-overrated)。因此,您的目标不应该是实现 100% 的代码覆盖率,而是确保您测试应用程序的所有相关场景。
85% 将是签入标准的良好起点。
我可能会为运输标准选择各种更高的标准——这取决于被测试的子系统/组件的重要性。
我使用 cobertura,无论百分比如何,我都建议保持 cobertura-check 任务中的值是最新的。至少,继续将 totallinerate 和 totalbranchrate 提高到略低于您当前的覆盖范围,但永远不要降低这些值。还将 Ant 构建失败属性与此任务联系起来。如果由于缺乏覆盖而导致构建失败,您知道有人添加了代码但尚未对其进行测试。例子:
<cobertura-check linerate="0"
branchrate="0"
totallinerate="70"
totalbranchrate="90"
failureproperty="build.failed" />
当我认为我的代码没有经过足够的单元测试,并且我不确定接下来要测试什么时,我会使用覆盖率来帮助我决定接下来要测试什么。
如果我增加单元测试的覆盖率 - 我知道这个单元测试值得。
这适用于未覆盖、50% 覆盖或 97% 覆盖的代码。
我更喜欢做 BDD,它结合了自动化验收测试、可能的其他集成测试和单元测试。对我来说,问题是整个自动化测试套件的目标覆盖范围应该是什么。
除此之外,答案取决于您的方法、语言以及测试和覆盖工具。在 Ruby 或 Python 中进行 TDD 时,保持 100% 的覆盖率并不难,而且非常值得这样做。管理 100% 的覆盖率比管理 90% 的覆盖率要容易得多。也就是说,填补出现的覆盖空白(并且在做好 TDD 时,覆盖空白很少见,通常值得您花时间),而不是管理您尚未解决和错过覆盖的覆盖空白列表要容易得多由于您不断发现未发现代码的背景而导致的回归。
答案还取决于您项目的历史。我只发现上述内容在从一开始就以这种方式管理的项目中是实用的。我已经大大提高了大型遗留项目的覆盖率,这样做是值得的,但我从来没有发现回过头来填补每个覆盖率空白是切实可行的,因为旧的未经测试的代码没有被很好地理解,无法正确地做到这一点并且迅速地。
如果您已经进行了相当长的单元测试,我认为没有理由不接近 95%+。然而,至少,我一直使用 80%,即使是刚开始测试时也是如此。
这个数字应该只包括项目中编写的代码(不包括框架、插件等),甚至可能不包括完全由调用外部代码编写的代码组成的某些类。这种调用应该被嘲笑/存根。
一般来说,从我读过的几篇工程卓越最佳实践论文来看,单元测试中的新代码 80% 是产生最佳回报的点。对于所付出的努力,超过该 CC% 会产生更少的缺陷。这是许多大公司使用的最佳实践。
不幸的是,这些结果中的大多数都是公司内部的,因此我没有可以向您指出的公开文献。
我对这个难题的回答是对可以测试的代码进行 100% 的行覆盖,对无法测试的代码进行 0% 的行覆盖。
我目前在 Python 中的做法是将我的 .py 模块分为两个文件夹:app1/ 和 app2/ 并在运行单元测试时计算这两个文件夹的覆盖率并目视检查(我必须有一天自动化)app1 具有 100% 的覆盖率和app2 的覆盖率为 0%。
当/如果我发现这些数字与标准不同时,我会调查并更改代码的设计,以使覆盖范围符合标准。
这确实意味着我可以推荐实现 100% 的库代码行覆盖率。
我也偶尔查看 app2/ 看看我是否可以在那里测试任何代码,如果可以的话,我可以将它移到 app1/
现在我不太担心总体覆盖率,因为根据项目的规模,这可能会有很大差异,但通常我已经看到了 70% 到 90% 以上。
使用 python,我应该能够设计一个冒烟测试,它可以在测量覆盖率的同时自动运行我的应用程序,并希望在将冒烟测试与单元测试数据结合起来时获得 100% 的总和。
代码覆盖率很好,但前提是您从中获得的好处超过了实现它的成本/努力。
一段时间以来,我们一直在努力达到 80% 的标准,但是我们刚刚决定放弃这一标准,而是更加专注于我们的测试。专注于复杂的业务逻辑等,
做出这个决定是因为我们花费了越来越多的时间来追踪代码覆盖率和维护现有的单元测试。我们觉得我们已经到了这样一个地步,即我们从代码覆盖率中获得的好处被认为低于我们为实现它所付出的努力。
简短回答:60-80%
长答案:我认为这完全取决于您项目的性质。我通常通过对每个实际部分进行单元测试来开始一个项目。到项目的第一个“版本”时,根据您正在执行的编程类型,您应该有一个相当不错的基本百分比。此时,您可以开始“强制执行”最低代码覆盖率。
查看Crap4j。这是一种比直接代码覆盖更复杂的方法。它将代码覆盖率测量与复杂性测量相结合,然后向您展示当前未测试的复杂代码。
从另一个角度查看覆盖率:具有清晰控制流的编写良好的代码是最容易覆盖、最容易阅读且通常错误最少的代码。通过编写清晰和可覆盖的代码,并通过与代码并行编写单元测试,恕我直言,您将获得最佳结果。
在我看来,答案是“这取决于你有多少时间”。我试图达到 100%,但如果我没有在我拥有的时间内完成它,我不会大惊小怪。
当我编写单元测试时,我戴的帽子与我在开发生产代码时戴的帽子不同。我考虑测试代码声称要做什么以及可能破坏它的情况是什么。
我通常遵循以下标准或规则:
单元测试应该是关于我的代码的预期行为的文档形式,即。给定特定输入的预期输出以及客户端可能想要捕获的可能引发的异常(我的代码的用户应该知道什么?)
单元测试应该帮助我发现我可能还没有想到的假设条件。(如何使我的代码稳定和健壮?)
如果这两个规则不能产生 100% 的覆盖率,那就这样吧。但是一旦有时间,我会分析未覆盖的块和行,并确定是否还有没有单元测试的测试用例,或者是否需要重构代码以消除不必要的代码。
这在很大程度上取决于您的应用程序。例如,某些应用程序主要由无法进行单元测试的 GUI 代码组成。
我认为不可能有这样的黑白规则。
应审查代码,特别注意关键细节。
但是,如果它没有经过测试,它有一个错误!
根据代码的重要性,75%-85% 的任何地方都是一个很好的经验法则。运输代码绝对应该比内部公用事业等进行更彻底的测试。
这必须取决于您处于应用程序开发生命周期的哪个阶段。
如果您已经进行了一段时间的开发并且已经实现了很多代码,并且现在才意识到您需要考虑代码覆盖率,那么您必须检查您当前的覆盖率(如果存在),然后使用该基线来为每个 sprint 设置里程碑(或在 sprint 期间的平均增长),这意味着在继续为最终用户提供价值的同时承担代码债务(至少根据我的经验,如果你增加了测试,最终用户一点也不在乎如果他们没有看到新功能,则覆盖)。
根据您的领域,拍摄 95% 并非不合理,但我不得不说平均而言,您将看到 85% 到 90% 的平均案例。
我认为正确的代码覆盖率的最佳症状是单元测试帮助解决的具体问题的数量合理地对应于您创建的单元测试代码的大小。
我认为最重要的是了解随着时间的推移覆盖趋势是什么,并了解趋势变化的原因。你认为趋势的变化是好是坏取决于你对原因的分析。
直到几天前,我们的目标是 >80%,但是在我们使用了大量生成代码之后,我们不关心 %age,而是让审阅者接听所需的覆盖率。
从Testivus 的帖子中,我认为答案上下文应该是第二个程序员。
从实际的角度来看,我们需要参数/目标来争取。
我认为这可以通过分析我们拥有的架构、功能(用户故事)的代码在敏捷过程中“测试”,然后得出一个数字。根据我在电信领域的经验,我会说 60% 是一个很好的检查值。