100%的代码覆盖率是一个梦想吗?


28

在繁重的jquery / backbonejs Web应用程序中期望100%的代码覆盖率是否可行?当实际代码覆盖率在javascript / jquery中徘徊在92%-95%左右时,由于未达到100%覆盖率而导致冲刺失败是否合理?


7
“冲刺失败”听起来很奇怪……
Donal Fellows 2012年

5
这是一个渐近线。
罗伯特·哈维

12
即使您有完整的报道,也不会发现某些错误,所以请不要依赖该数字来解决所有问题
棘手的怪癖

11
一切皆有可能。真正的问题是100%代码覆盖率的值是否值得花费时间和资源。
JohnFx 2012年

5
当根本的假设(即100%(或任何其他数字)的自动测试覆盖率将神奇地使您的代码变得更好)时,您为什么为此担心呢?
梅森惠勒2012年

Answers:


30

这既是现实的也是不现实的。

现实
如果您已经证明自动化测试可以覆盖整个代码库,那么坚持100%的覆盖率是合理的。
这也取决于项目的重要性。越关键,期望/要求完整的代码覆盖范围就越合理。
对于中小型项目,这样做更容易。

不切实际
您从0%的覆盖率开始...
该项目异常复杂,包含许多难以重新创建或触发的错误路径。
管理层不愿意承诺/投资以确保覆盖范围。

我从事的项目范围从无覆盖到体面。从来没有一个100%的项目,但是我有时候确实希望我们能接近100%。
最终,问题是现有的覆盖范围是否满足团队所需的足够情况,以使团队能够轻松地运输产品。

我们不知道失败对项目的影响,因此我们不能说92%或95%是否足够,还是真的需要100%。或就此而言,100%会完全测试您期望的一切。


30
...并且仅仅因为您具有100%的代码覆盖率并不意味着您具有100%的分支覆盖率,所以即使具有100%的代码覆盖率,您也可能会遗漏很多东西。
Bryan Oakley 2012年

3
+1代表专案大小。分解成更小的,可重复使用的和可测试的组件使我们自己获得了约95%的覆盖率。不需要100%的覆盖率。集成测试应弥补单元测试的空白。
Apoorv Khurasia 2012年

5
@BryanOakley ...而且您的测试可能毫无意义,甚至无法测试任何内容
David_001 2012年

5
@BryanOakley即使分支覆盖率为100%,分支的某种组合也可能会引起问题。(例如,可以在单独的测试中将两个顺序的IF语句分支到分支中,但会丢失同时输入两个分支的测试。完整的分支覆盖范围,但是缺少一条执行路径)
Izkata 2012年

4
甚至包括所有执行路径在内的100%分支覆盖率也是不够的。也许只有当您采用分支的某种组合并且有一些外部输入(例如,日期格式错误)时,才会发生一些错误。不可能涵盖所有情况。同时,如果覆盖率不足100%,但可以适当选择边缘条件作为输入,就可以拥有良好的信心。
Andrea 2012年

32

谁测试测试?

即使从理论上讲,它充其量也是非常幼稚的和不现实的,而在业务上则是不切实际的

  • 对于具有高循环复杂度的代码,这是不现实的。变量太多,无法涵盖每种组合。
  • 对于大量并发的代码,这是不现实的。该代码不是确定性的,因此您无法涵盖可能发生的每种情况,因为行为会在每次测试运行时发生变化。
  • 从业务角度讲,这是不现实的,它只是为关键路径代码(即重要的代码和可能经常更改的代码)编写测试而付出的实际报酬。

测试每一行代码不是一个好目标

编写测试非常昂贵,必须编写代码并对其进行自我测试,必须在其实际尝试测试的内容中记录这些代码,必须通过业务逻辑更改来维护该代码,并且测试失败,因为它们已过时。维护自动化测试和有关它们的文档可能比维护代码有时更昂贵。

这并不是说单元测试和集成测试没有用,而是仅在有意义的地方使用,而在可以杀死人员的行业之外,尝试测试代码库中的每一行代码都没有意义。在这些致命的杀伤力之外,很多人会快速编写代码库,因此无法计算出100%代码覆盖率所带来的正投资回报。

停止问题:

在可计算性理论中,停顿问题是根据任意计算机程序的描述和输入确定程序将结束运行还是继续永久运行的问题。

艾伦·图灵(Alan Turing)在1936年证明,不存在解决所有可能的程序输入对的停止问题的通用算法。证明的关键部分是计算机和程序的数学定义,这被称为图灵机。在图灵机上无法停止问题。这是决策问题的第一个例子。

既然您甚至无法证明某事有效100%,为什么要实现目标?

简单明了,在大多数情况下,没有任何商业意义。


7
这确实需要被接受。100%的代码覆盖率几乎与0%一样糟糕。
Ryathal 2012年

1
“变量太多,无法涵盖每种组合。” 这与获得100%的代码覆盖率无关。如果一行代码很重要,足以被编写,并且足够重要,可以四处走动,那么它就足够重要,可以被测试覆盖。如果测试未涵盖它,则唯一安全的假设是它不起作用。确实,对于某些代码,从业务角度进行测试没有任何意义。从业务角度来看,这是完全没有道理的代码。
still_dreaming_1

2
因此,您认为编写测试用例以覆盖getXXX()/setXXX()值对象的简单或简单的赋值构造函数是对时间和资源的良好利用,很遗憾,事实并非如此,这是一种非常幼稚的观点,缺乏现实世界的经验来进行备份。请记住,测试代码仍然是必须维护的代码。在每种情况下,编写用于解决问题的代码越少越好。

Uhm“对于具有高循环复杂性的代码来说,这是不现实的。变量太多,无法涵盖每个组合。” -当然,这就是为什么您必须将这样的代码分成较小的部分,这些部分的循环复杂度较小,因此更易于测试。以这种方式进行重构对于测试至关重要,它使测试更加容易。
PredragStojadinović18年

17

在大多数情况下,100%的代码覆盖率意味着您已经“作弊”了一点:

  • 系统的复杂,频繁更改的部分(如gui)已移至声明性模板或其他DSL。
  • 所有涉及外部系统的代码已被库隔离或处理。
  • 其他依赖性也是如此,尤其是那些需要副作用的依赖性。

基本上,难于测试的部件已被转移到不一定被视为“代码”的区域。这并不总是现实的,但是请注意,所有这些实践都独立于帮助您进行测试之外,使您的代码库更易于使用。


如何将事物转移到DSL作弊?
back2dos 2012年

2
@ back2dos-尽管您可以对嵌入式python脚本进行单元测试,但您可能未对html模板或CSS进行单元测试,也未对其覆盖范围的计数进行覆盖估计。
Dan Monego 2012年


9

即使对于新项目,对于特定应用程序所有部分的单元测试,100%的代码覆盖率也是梦dream以求的。我希望是这样,但是有时候,无论您多么努力地提取外部依赖关系,您都无法覆盖一段代码。例如,假设您的代码必须调用Web服务。您可以将Web服务调用隐藏在界面后面,以便可以模拟该部分,并测试Web服务之前和之后的业务逻辑。但是需要调用Web服务的实际部分不能进行单元测试(无论如何都很好)。另一个示例是是否需要连接到TCP服务器。您可以隐藏连接到接口后面的TCP服务器的代码。但是物理连接到TCP服务器的代码无法进行单元测试,因为如果由于某种原因它关闭了,那将导致单元测试失败。而且,无论何时调用单元测试,都应始终通过。

一个好的经验法则是,您所有的业务逻辑都应具有100%的代码覆盖率。但是必须调用外部组件的部分,它应该具有尽可能接近100%的代码覆盖率。如果您无法到达,那我就不会流汗。

更重要的是,测试是否正确?它们是否准确反映了您的业务和要求?如果您所做的只是测试不正确或测试代码不正确,那么仅具有代码覆盖率就没有任何意义。话虽这么说,如果您的测试很好,那么覆盖率达到92-95%是杰出的。


1
测试当您遇到错误情况和失败响应的奇怪组合时发生的事情可能非常棘手。
Donal Fellows 2012年

难道当您遇到这些棘手问题时,您是否理解系统将做什么,这是单元测试的吸引力之一吗?另外,单元测试和集成测试之间也存在一些混淆。
彼得·史密斯

unit testing与此混为一谈integration testing,您未编写的测试代码正在integration测试。TCP堆栈位于您不应该进行测试的操作系统中,您应该假定它已经被编写者进行过测试。

4

我要说的是,除非代码的设计目标是允许100%的测试覆盖率,否则100%可能无法实现。原因之一是,如果您进行防御性编码(应该这样做),则有时应使用能处理某些情况的代码,这些情况在您对系统有所了解的情况下就确定不会发生或不会发生。从定义上讲,要用测试覆盖这样的代码将非常困难。没有这样的代码可能很危险-如果您输入错了并且这种情况确实发生了256次,该怎么办?如果不相关的地方发生变化而使不可能的事情变为可能怎么办?等等,因此100%可能很难通过“自然”的方式达到-例如,如果您有分配内存的代码并且您有检查其是否失败的代码,除非您嘲笑了内存管理器(这可能不容易)并编写一个返回“内存不足”的测试,以覆盖该代码可能很困难。对于JS应用程序,它可能是围绕不同浏览器中可能出现的DOM怪癖,外部服务的可能失败等的防御性编码。

因此,我要说的是,我们应该争取尽可能地接近100%,并且有充分的理由来解释差异,但我认为未必会因为失败而无法准确地获得100%的收益。95%可以在大型项目中很好,取决于5%。


仅仅因为代码不应在正常情况下在生产中运行并不意味着它不能以测试运行的方式编写。要使异常代码正常运行有多重要?通过测试覆盖它是否足够重要?我认为,如果要通过测试覆盖不那么重要,那么处理该案例也就不够重要。不需要测试的代码是不需要存在的代码,应将其删除。
still_dreaming_1

2

如果您是从一个新项目开始的,并且严格使用测试优先的方法,那么完全100%的代码覆盖率是完全合理的,因为在测试完成后某个时刻所有代码都将被调用被执行。但是,由于方法的可见性,您可能未直接对每种方法或算法进行显式测试,并且在某些情况下,甚至可能没有间接测试某些方法。

对100%的代码进行测试可能是一项代价高昂的工作,特别是如果您尚未设计允许您实现此目标的系统,并且如果您将设计工作重点放在可测试性上,那么您可能没有给予足够的关注设计应用程序以满足其特定要求,特别是在项目规模较大的情况下。对不起,但是您不能在没有任何妥协的情况下同时使用这两种方法。

如果将测试引入到以前从未维护过或未进行过测试的现有项目中,那么如果不付出成本来付出努力,就不可能获得100%的代码覆盖率。您可以期望的最好的结果就是提供对代码最关键部分的测试覆盖率。

当实际代码覆盖率在javascript / jquery中徘徊在92%-95%左右时,由于未达到100%覆盖率而导致冲刺失败是否合理?

在大多数情况下,我想说的是,如果您没有达到目标,则应该只将冲刺视为“失败”。实际上,我不希望将sprint视为失败,因为在下一次定义sprint时,为了使您的计划正确,您需要能够从不符合期望的sprint中学习。无论如何,我认为将代码覆盖率作为sprint相对成功的一个因素是不合理的。您的目标应该是做足够的工作以使所有功能都按指定的方式工作,如果您是测试优先的编码人员,那么您应该能够确信自己的测试将支持此目标。您认为可能需要添加的任何其他测试实际上都是糖衣,因此会增加费用,使您无法满意地完成冲刺。


“很抱歉,但是您不能在没有任何妥协的情况下同时做到这两种方式。” 那是不对的。您始终可以缩小功能,或者放慢速度。如果某项不值得测试,则不值得编写。如果一行代码足够重要,可以进行测试,那么它就足够重要。如果测试不够重要,那么保持环境不够重要。未经测试的代码行的唯一安全假设是,它行不通。
still_dreaming_1

@ still_dreaming_1,您似乎支持我的说法,并且自相矛盾。缩减功能或更改截止日期是折中的选择,每一个都会影响项目成本和利益相关者的期望。测试以前未经过全面测试的遗留代码非常困难,因为您不仅需要理解其运行时的代码,还必须了解原始创建者的意图,并且编写捕获现有遗留代码行为的测试并不一定表明该代码完全可以按预期工作。
S.Robins

我想我的意思是,由于开发速度越来越快而遭到破坏的某些东西,尚未创建的功能或更改并不是真正的妥协,因为如果您为了快速移动而失去了覆盖范围,这些功能和更改可能会仅假定反正不能正常工作。那么,如果这些更改是否正常工作不重要,那么进行更改或添加这些功能又有什么意义呢?如果它们是否工作正常并不重要,则不需要进行这些更改,现在应将其从代码中删除。
still_dreaming_1

我不再完全相信,或者至少我意识到您所说的事实的实用性,尤其是在旧代码库中,所以这只是我当时试图提出的观点的一种解释。实际上,我现在甚至一直都在新的代码库上进行TDD,这完全是矛盾的,更不用说获得100%的覆盖率了。一方面,每种形式的逻辑和推理都告诉我,这些东西都应该是对的,但实际上,我似乎无法使其实用。因此,编程世界有些问题,我们需要一个新的范例。
still_dreaming_1 '18

1

我当然不会这样做,但是我已经在两个大型项目中做到了。如果您已经建立了一个用于单元测试的框架,这并不难,但是它确实可以进行很多测试。

您是否遇到一些特殊的障碍,阻止您达到最后几行?如果不是这样,那么从95%覆盖到100%覆盖率很简单,那么您最好去做。既然你在这里问,我会认为有什么。那是什么东西


这是这里最好的答案之一。问什么使一行代码难以覆盖是一个好问题。覆盖这些行将迫使您改进代码以实现它,所以这将是双赢。
still_dreaming_1

0

92%可以。我认为真正的问题是:

  • 现在有92%是“新”规范吗?如果下一个Sprint进行了88%的测试,那可以吗?这通常是放弃测试套件的开始。

  • 该软件有效且没有错误的重要性。您出于这些原因进行测试,而不是“出于测试目的”

  • 是否有计划返回并填写缺失的测试?

  • 你为什么要测试?似乎重点是线路覆盖的百分比而不是功能


“软件正常工作且没有错误的重要性”?好问题。错误的定义是什么?无法正常工作的东西。如果某些代码不能正常工作是可以的,请不要编写它。代码的全部要点是它可以工作。
still_dreaming_1

0

马丁·福勒(Martin Fowler)在他的博客中写道:I would be suspicious of anything like 100% - it would smell of someone writing tests to make the coverage numbers happy, but not thinking about what they are doing.

但是,甚至有一些标准要求在单位级别100%覆盖。例如,这是欧洲航天界标准(ECSS,欧洲空间标准化合作组织)的要求之一。此处链接的论文讲述了一个有趣的项目故事,该项目的目标是在已经完成的软件中达到100%的测试覆盖率。它基于与开发单元测试的相关工程师的意见。

一些课程是:

  • 100%的覆盖率不寻常,但可以实现
  • 有时有必要100%覆盖
  • 100%覆盖带来新风险
  • 不要针对100%指标进行优化
  • 制定适当的策略以最大程度地覆盖
  • 100%的覆盖率不足以保证高质量

0

也许问问是否可行和合理并不是要问的最有帮助的问题。可能最实用的答案是已接受的答案。我将在更哲学的层面上对此进行分析。

100%的覆盖率是理想的,但是理想情况下,它不是必需的,或者更容易实现。我宁愿考虑它是否自然和人性,而不是可行或合理的。

使用当今的工具几乎无法实现正确编程的作用。编写完全正确且没有错误的代码非常困难。这只是不自然的。因此,在没有其他明显选择的情况下,我们将转向TDD和跟踪代码覆盖率之类的技术。但是,只要最终结果仍然是不自然的过程,您将很难使人们始终如一地快乐地去做。

实现100%的代码覆盖率是不自然的。对于大多数人来说,强迫他们做到这一点将是一种折磨。

我们需要能够映射到我们自然思维模型的流程,工具,语言和代码。如果我们做不到这一点,就无法测试产品的质量。

只需看看今天的所有软件即可。其中大多数都非常定期地混乱。我们不想相信这一点。我们想相信我们的技术是神奇的,并使我们感到高兴。因此,在大多数情况下,我们选择忽略,原谅和忘记我们的技术。但是,如果我们对事情做一个诚实的评估,那么今天的大多数软件都是糟糕透顶的。

以下是使编码更自然的一些努力:

https://github.com/jcoplien/trygve

https://github.com/still-dreaming-1/PurposefulPhp

后者是极其不完整和实验性的。实际上,这是我开始的一个项目,但是我相信,如果我能够花时间去完成它,那对编程技术将是一个巨大的进步。基本上是这样一种想法,即如果合同只表达我们关心的类行为的各个方面,并且我们已经将合同表达为代码,那么为什么不仅要在合同中同时包含类和方法定义。这样,合同就是代码,我们不需要实现所有方法。让图书馆弄清楚如何为我们履行合同。


-2

在新代码上达到100%应该是可以实现的,并且如果您正在练习TDD,则很可能默认情况下会达到目标,因为您非常刻意为生产代码的每一行编写测试。

在没有单元测试的情况下编写的现有遗留代码上可能会很困难,因为遗留代码通常不是在考虑单元测试的情况下编写的,并且可能需要大量重构。鉴于风险和时间表的现实性,这种级别的重构通常不切实际,因此您需要权衡取舍。

在我的团队中,我指定100%的代码覆盖率,并且如果我们发现少于代码审查中的代码覆盖率,则该组件的技术所有者讨论为什么未与开发人员达成100%的覆盖率,并且必须与开发人员的推理保持一致。通常情况下,如果遇到100%问题,开发人员会在代码审查之前与技术所有者联系。我们发现,一旦养成习惯并学习解决一些常见问题的技术,即向遗留代码中添加定期定期命中100%的测试并不像您最初想象的那样困难。

迈克尔·费瑟(Michael Feather)的书“ 有效地使用旧版代码 ”对于我们提出将测试添加到旧版代码中的策略来说,对我们来说是无价的。


-3

不,这是不可能的,而且永远不会。如果有可能,所有的数学都将陷入有限主义。例如,如何测试一个接受两个64位整数并将它们相乘的函数?测试和证明程序正确始终是我的问题。对于除最琐碎的程序以外的任何程序,测试基本上是无用的,因为它仅覆盖少数情况。这就像检查1,000个数字并说您已经证明了哥德巴赫猜想。


哦! 所以有人感到沮丧,我没有从概念上回答这个问题。测试是一种浪费……我不在乎它是否受欢迎。它永远都行不通。这不可以。最聪明的计算机科学家都知道这一点(Dijkstra,Knuth,Hoare等人)。我想,如果您是一名JavaScript程序员,那么他们就不会在乎那些极端。布拉,无论如何,谁在乎……编写糟糕的代码。浪费CO ^ 2进行测试。—我的意思是,谁有时间坐下来思考了?我们已经将其导出到计算机。
非常愚蠢的2012年

3
该问题被标记为“ TDD”。TDD不仅仅是测试工具,更是一种设计工具和问题探索工具,每个“测试”仅是代码在特定上下文中的行为示例,以便人们可以阅读和理解正在发生的事情,然后安全地进行更改。 。TDD做得好往往会导致代码更简洁,更易于使用,并且运行测试只是检查文档是否为最新。大多数TDD套件几乎都不会发现bug;这不是他们的目的。我认为您之所以感到沮丧,是因为您的回答出了言而又缺乏理解,我希望这个评论对您有所帮助。
鲁尼武尔(Lunivore),2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.