如果您要为单元测试规定最小百分比的代码覆盖率,甚至可能要求作为提交到存储库的要求,那将是什么?
请说明您是如何得出答案的(因为如果您所做的只是选择一个号码,那么我本来可以自己完成所有的事情;)
如果您要为单元测试规定最小百分比的代码覆盖率,甚至可能要求作为提交到存储库的要求,那将是什么?
请说明您是如何得出答案的(因为如果您所做的只是选择一个号码,那么我本来可以自己完成所有的事情;)
Answers:
阿尔贝托·萨沃亚(Alberto Savoia)的散文恰好回答了这个问题(以一种非常有趣的方式!):
http://www.artima.com/forums/flat.jsp?forum=106&thread=204677
关于测试范围的证词
一大早,一位程序员问大师:
“我准备编写一些单元测试。我应该针对什么代码覆盖率?”
大师回答:
“不用担心覆盖率,只需编写一些好的测试即可。”
程序员微笑着,鞠躬,然后离开。
...
那天晚些时候,第二个程序员问了同样的问题。
大师指着一壶开水说:
“我应该在那个锅里放几粒米?”
程序员困惑不解地回答:
“我怎么可能告诉你?这取决于您需要养活多少人,他们有多饿,所提供的其他食物,有多少米饭等等。”
“是的,”大师说。
第二个程序员微笑着,鞠躬,然后离开。
...
一天快结束时,第三位程序员来了,问了有关代码覆盖率的同样问题。
“百分之八十,而且不少!” 船长用严厉的声音回答,把拳头砸在桌子上。
第三位程序员微笑着,鞠躬然后离开。
...
在最后的答复之后,一个年轻的学徒向这位大师求助:
“太好了,今天我无意中听到您用三个不同的答案回答有关代码覆盖率的相同问题。为什么?”
伟大的大师从椅子上站起来:
“来和我一起喝点新鲜的茶,让我们谈谈。”
在他们的杯子里放满冒烟的绿茶后,伟大的大师开始回答:
“第一个程序员是新手,刚刚开始测试。现在他有很多代码,没有测试。他还有很长的路要走。此时专注于代码覆盖将是令人沮丧的,并且非常无用。他最好习惯于编写和运行一些测试。他稍后可能会担心报道。”
“另一方面,第二位程序员在编程和测试方面都具有丰富的经验。当我问她一个锅中应该放多少米米时,我帮助她意识到必要的测试量取决于许多因素,而且她比我更了解这些因素–毕竟这是她的代码。没有一个简单,简单的答案,而且她足够聪明,可以处理事实并以此为依据。”
这位年轻的学徒说:“我明白了,但是,如果没有一个简单的答案,那你为什么要回答第三位程序员“百分之八十”?
伟大的大师大声笑着,使他的肚子大笑起来,这证明他不仅喝绿茶,而且还上下摇摆。
“第三位程序员只需要简单的答案,即使没有简单的答案……也不会遵循它们。”
年轻的徒弟和灰头土脸的大师在沉思的沉默中喝完了茶。
如果您的目标是100%的覆盖率(而不是对所有功能进行100%的测试),则代码覆盖率是一个令人误解的指标。
因此,请相信您自己或您的开发人员要彻底,并涵盖其代码中的所有路径。务实,不要追求100%的神奇覆盖率。如果您对代码进行TDD开发,则应该获得90%以上的覆盖率作为奖励。使用代码覆盖率突出显示您错过的代码块(但是,如果您使用TDD,则不会发生..因为您编写代码只是为了通过测试。没有伙伴测试就没有代码。)
代码覆盖范围很大,但功能覆盖范围则更好。我不相信涵盖我所写的每一行。但是,我确实相信要对我要提供的所有功能进行100%的测试覆盖率(即使是我自带的超酷功能,在会议中也没有讨论)。
我不在乎是否会有测试未涵盖的代码,但我会在乎是否重构代码并最终获得不同的行为。因此,我唯一的目标就是100%的功能覆盖率。
公认的答案很有意义-没有一个数字可以作为每个项目的标准。有些项目只是不需要这样的标准。我认为,所接受的答案不足之处在于描述了如何针对特定项目做出决定。
我会照做的。我不是测试工程专家,很高兴看到更明智的答案。
首先,您为什么首先要强加这样的标准?通常,当您想在过程中引入经验信心时。我所说的“经验信心”是什么意思?好吧,真正的目标正确性。对于大多数软件,我们不可能在所有输入中都知道这一点,因此我们同意说代码已经过充分测试。这是可以理解的,但是仍然是一个主观标准:它将始终对您是否达到要求进行辩论。这些辩论是有用的,应该发生,但也暴露出不确定性。
代码覆盖率是一种客观的衡量标准:一旦您看到覆盖率报告,就不会对是否满足标准产生歧义。它证明正确吗?完全没有,但是它与代码的测试程度之间有着明确的关系,而这又是我们提高对其正确性的信心的最佳方法。代码覆盖率是我们所关注的无与伦比的质量的可度量近似值。
拥有经验标准可以增加价值的一些特定情况:
代码覆盖率不是一个单独的指标。有几种不同的衡量覆盖率的方法。您可以设置哪个标准取决于您使用该标准要满足的条件。
我将以两个常见指标作为示例,说明何时可以使用它们来设置标准:
if
)时,是否评估了两个分支?这样可以更好地理解您的代码的逻辑覆盖范围:经过测试,我的代码可能采用的几种可能路径?
还有许多其他指标(例如,行覆盖率类似于语句覆盖率,但是对于多行语句产生不同的数值结果;条件覆盖率和路径覆盖率与分支覆盖率相似,但是反映了对以下各项可能排列的更详细的视图)您可能会遇到的程序执行问题。)
最后,回到最初的问题:如果设置代码覆盖率标准,那么该数字应该是多少?
希望在这一点上很明显,我们正在谈论一个近似值,因此,我们选择的任何数字本质上都是近似值。
一些可能选择的数字:
在实践中,我还没有看到低于80%的数字,并且很难想象要设置这些数字的情况。这些标准的作用是增强对正确性的信心,低于80%的数字并不是特别能激发信心。(是的,这是主观的,但是再次,您的想法是在设置标准时做出一次主观选择,然后再使用客观的度量方法。)
以上假设正确性是目标。代码覆盖范围只是信息;它可能与其他目标有关。例如,如果您担心可维护性,那么您可能会关心松耦合,这可以通过可测试性来证明,而可测试性又可以通过代码覆盖率(以某些方式)进行度量。因此,您的代码覆盖率标准也为近似“可维护性”的质量提供了经验基础。
我最喜欢的代码覆盖率是100%带有星号。星号出现是因为我更喜欢使用允许我将某些行标记为“不计数”的行的工具。如果我覆盖了“计数”行的100%,我就完成了。
基本过程是:
这样,如果我和我的合作者将来添加新代码或更改测试,就会有一条亮线告诉我们是否错过了重要事项-覆盖率降至100%以下。但是,它也提供了处理不同测试优先级的灵活性。
// @codeCoverageIgnore
并将其从覆盖率中排除。
我想分享另一个有关测试范围的内容。
我们有一个庞大的项目,在Twitter上,我注意到在700个单元测试中,我们只有20%的代码覆盖率。
是正确的20%吗?是20%代表您的用户最喜欢的代码吗?您可能会再添加50个测试,而仅添加2%。
对于一个设计良好的系统,从一开始单元测试就推动了开发,我会说85%是一个相当低的数字。设计成可测试的小类应该比这更好地覆盖。
用以下类似的方法很容易消除这个问题:
是的,但是关于代码覆盖率有一些重要的要点。以我的经验,正确使用此指标实际上非常有用。话虽如此,我还没有看到所有系统,并且我敢肯定其中有很多系统很难看到代码覆盖率分析增加了任何实际价值。代码看起来可能如此不同,可用测试框架的范围也可能有所不同。
另外,我的推理主要涉及相当短的测试反馈循环。对于我正在开发的产品,最短的反馈回路非常灵活,涵盖从类测试到进程间信令的所有内容。测试可交付的子产品通常需要5分钟,并且对于如此短的反馈循环,确实可以使用测试结果(尤其是我们在此处查看的代码覆盖率指标)来拒绝或接受存储库中的提交。
使用代码覆盖率度量标准时,您不仅应具有必须满足的固定(任意)百分比。我认为这样做并不能给您代码覆盖率分析的真正好处。而是定义以下指标:
仅当我们没有超出LWM并且我们没有低于HWM时,才能添加新代码。换句话说,不允许减少代码覆盖率,而应覆盖新代码。请注意我怎么说应该和不应该(下面解释)。
但这是否意味着将无法清除不再使用的经过良好测试的旧垃圾?是的,这就是为什么您必须对这些事情务实。在某些情况下,必须打破规则,但是对于我的典型日常集成而言,我的经验是这些指标非常有用。他们给出了以下两个含义。
可测试的代码被提升。在添加新代码时,您实际上必须尽力使代码可测试,因为您将不得不尝试用测试用例覆盖所有代码。可测试的代码通常是一件好事。
随着时间的流逝,遗留代码的测试覆盖率不断增加。当添加新代码而又无法用测试用例覆盖时,可以尝试覆盖一些旧代码而不是绕过LWM规则。有时这种必要的作弊行为至少会带来积极的副作用,即遗留代码的覆盖范围会随着时间的推移而增加,从而使这些规则看似严格的执行在实践中相当实用。
再者,如果反馈回路太长,则在集成过程中设置类似的内容可能完全不可行。
我还想提到代码覆盖率度量标准的两个更一般的优点。
代码覆盖率分析是动态代码分析的一部分(与静态代码分析(即Lint)相反)。在动态代码分析期间(通过诸如purify系列之类的工具,http ://www-03.ibm.com/software/products/en/rational-purify-family)发现的问题是未初始化的内存读取(UMR),内存泄漏等。只有在执行的测试用例覆盖了代码的情况下,才能发现这些问题。在测试用例中最难覆盖的代码通常是系统中的异常情况,但是如果您希望系统正常地失败(即错误跟踪而不是崩溃),则可能需要付出一些努力来覆盖异常情况在动态代码分析中也是如此。如果运气不好,UMR可能导致段错误或更糟。
人们为保留100%的新代码而感到自豪,并且人们以与其他实现问题类似的热情讨论测试问题。如何以更可测试的方式编写此函数?您打算如何处理这种异常情况,等等。
否定性,是为了完整性。
许多商店不重视测试,因此,如果您的价值大于零,那么至少会有一定的价值升值-因此可以说非零并不坏,因为许多商店仍然为零。
在.Net世界中,人们经常说80%是合理的。但是他们在解决方案级别上这么说。我更喜欢在项目级别进行度量:如果您具有Selenium等或手动测试,则对于UI项目可能是30%很好,对于数据层项目则可能是20%,但是对于企业来说95%以上可能是可以实现的规则层,如果不是完全必要的话。因此,总体覆盖率可以达到60%,但是关键业务逻辑可能要高得多。
我也听说过:向往100%,您将达到80%;但希望达到80%,那么您将达到40%。
底线:应用80:20规则,让应用的错误计数指导您。
代码覆盖率只是另一个指标。就其本身而言,这可能会非常令人误解(请参阅www.thoughtworks.com/insights/blog/are-test-coverage-metrics-overrated)。因此,您的目标不应是实现100%的代码覆盖率,而应确保测试应用程序的所有相关方案。
85%将是签入标准的良好起点。
我可能会选择各种更高的标准作为运输标准-取决于所测试的子系统/组件的重要性。
我使用cobertura,无论使用什么百分比,我都建议保持cobertura-check任务中的值最新。至少应将totallinerate和totalbranchrate至少提高到当前覆盖率以下,但不要降低这些值。还要将Ant build failure属性绑定到该任务。如果由于缺少覆盖而导致构建失败,则说明您知道某人已添加代码,但尚未对其进行测试。例:
<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%的人合作。
此数字应仅包括项目中编写的代码(不包括框架,插件等),甚至可能排除某些类,这些类完全由对外部代码的调用编写的代码组成。这种呼叫应被模拟/打桩。
对于这个难题,我的答案是使您可以测试的代码具有100%的行覆盖率,而无法测试的代码具有0%的行覆盖率。
我目前在Python中的做法是将.py模块划分为两个文件夹:app1 /和app2 /,并在运行单元测试时计算这两个文件夹的覆盖率,并目视检查(我有一天必须自动执行此操作)app1的覆盖率是100%, app2的覆盖率为0%。
当/如果我发现这些数字与标准不符,我会调查并更改代码的设计,以使覆盖范围符合标准。
这确实意味着我可以建议实现库代码的100%行覆盖率。
我偶尔也会查看app2 /,以查看是否可以在此处测试任何代码,如果可以,可以将其移至app1 /
现在,我不必太担心总覆盖率,因为总覆盖率可能会因项目规模的不同而有很大差异,但总的来说,我已经看到70%到90%以上。
使用python,我应该能够设计出一种烟雾测试,该烟雾测试可以在测量覆盖率的同时自动运行我的应用程序,并希望在将烟雾测试与单元测试数据结合起来时获得100%的总和。
从另一个角度查看覆盖范围:具有清晰控制流的编写良好的代码最容易覆盖,最容易阅读,并且通常是错误最少的代码。通过在编写代码时要牢记清晰性和可覆盖性,并通过与代码并行编写单元测试来获得最佳效果。
我认为答案是“这取决于您有多少时间”。我试图达到100%,但是如果我没有时间的话就不会大惊小怪。
编写单元测试时,与开发生产代码时所戴的帽子相比,我戴的帽子与其他人不同。我考虑经过测试的代码声称要做什么以及可能破坏它的情况是什么。
我通常遵循以下条件或规则:
单元测试应该是关于我的代码的预期行为的文档形式,即 给定特定输入的预期输出以及客户端可能想捕获的异常(我的代码的用户应该知道什么?)
单元测试应该可以帮助我发现我可能尚未想到的情况。(如何使我的代码稳定可靠?)
如果这两个规则不能产生100%的覆盖率,那就可以了。但是一旦有时间,我就会分析未发现的块和行,并确定是否仍然存在没有单元测试的测试用例,或者是否需要重构代码以消除不必要的代码。
这在很大程度上取决于您的应用程序。例如,某些应用程序主要由无法进行单元测试的GUI代码组成。