测试覆盖率是否足以衡量代码质量?


20

如果我有一些代码具有80%的测试覆盖率(所有测试均通过),是否可以说它比没有测试覆盖率的代码具有更高的质量?

还是公平地说它更易于维护?


2
100%的覆盖率并不意味着它已经过测试。但是0%表示它尚未经过测试。
mouviciel 2010年

1
技术上没有。实际上,是的。经验告诉很多软件工程师和测试人员,当代码覆盖率达到80%左右时,足以进行单元测试的缺陷类型便开始趋于平稳。这是pareto原则。基本上,一旦覆盖了80%的代码,无论测试的质量如何,您就可能已经相当彻底地测试了导致大多数潜在问题的20%的代码。这不是绝对的,而是传统的智慧。如果生活取决于您的测试,则必须更加彻底。
Calphool 2014年

@JoeRounceville我不确定...我可以实现高测试覆盖率,而没有测试真正有用的东西。覆盖率仅告诉您测试套件所涉及的代码量,而不是测试是否有意义。
Andres F.

1
@AndresF。这就是为什么我说“技术上不,实际上是”。人们不是白痴(通常)。他们不会(通常)只测试不费吹灰之力的案例。因此,根据经验,许多商店停靠了大约80%的覆盖率,这(相当安全)假设他们的员工不是白痴。
Calphool

Answers:


24

从严格意义上讲,在确定测试套件的质量之前,不主张任何权利是不公平的。如果大多数测试相互无关紧要,那么通过100%的测试就没有意义。

问题是:在项目的历史中,这些测试中有没有发现bug?测试的目的是发现错误。如果没有,他们将无法通过测试。他们可能只会给您一种错误的安全感,而不是提高代码质量。

为了改善测试设计,您可以使用(1)白盒技术,(2)黑盒技术和(3)变异测试。

(1)以下是一些适用于您的测试设计的优秀白盒技术。白盒测试是在考虑特定源代码的情况下构建的。白盒测试的一个重要方面是代码覆盖率:

  • 是否调用了每个函数?[功能范围]
  • 每个语句都执行了吗?[声明覆盖范围–功能覆盖范围和语句覆盖范围都是非常基本的,但总比没有好]
  • 对于每个决定(例如ifwhile),您是否有一个测试将其强制为真,而其他测试将其强制为假?[决定范围]
  • 对于每个是合取(uses &&)或析取(uses ||)的条件,每个子表达式是否都有对真/假的检验?[条件报道]
  • 循环覆盖:您是否进行了强制0次,1次,2次迭代的测试?
  • 每个break循环都覆盖了吗?

(2)当需求可用时使用黑盒技术,但代码本身不可用。这些可以导致高质量的测试:

  • 您的黑盒测试涵盖多个测试目标吗?您将希望测试是“胖”的:它们不仅测试功能X,而且还测试Y和Z。不同功能的交互是发现bug的好方法。
  • 唯一不希望进行“胖”测试的情况是在测试错误情况时。例如,测试无效的用户输入。如果您试图实现多个无效的输入测试目标(例如,无效的邮政编码和无效的街道地址),则可能是一种情况掩盖了另一种情况。
  • 考虑输入类型,并为输入类型形成“等效类”。例如,如果您的代码进行测试以查看三角形是否等边,则使用带有边(1、1、1)的三角形的测试可能会发现与测试数据(2、2、2)和(3,3,3)将找到。最好将时间花在思考其他类别的输入上。例如,如果您的程序要处理税收,则需要对每个税收等级进行测试。[这称为等效分区。]
  • 特殊情况通常与缺陷有关。测试数据还应具有边界值,例如等效任务边缘上方,上方或下方的边界值。例如,在测试排序算法时,您将要测试一个空数组,一个单元素数组,一个包含两个元素的数组以及一个非常大的数组。您不仅应考虑边界情况,而且还应考虑边界情况。[这就是所谓的边界值分析。]
  • 另一种技术是“错误猜测”。如果您尝试一些特殊的组合可以使程序中断,您是否有这种感觉?然后尝试一下!请记住:您的目标是发现错误,而不是确认程序有效。有些人有错误猜测的诀窍。

(3)最后,假设您已经对白盒覆盖率进行了许多不错的测试,并应用了黑盒技术。你还能做什么?现在该测试您的测试了。可以使用的一种技术是变异测试。

在变异测试下,您希望对程序进行修改(以作为副本),以期创建一个错误。突变可能是:

将一个变量的引用更改为另一个变量;插入abs()函数;从小于变为大于;删除声明;用常量替换变量;删除覆盖方法;删除对超级方法的引用;更改参数顺序

在程序的各个位置创建数十个变体(程序仍需要编译才能进行测试)。如果您的测试没有找到这些错误,那么您现在需要编写一个可以在程序的变异版本中找到该错误的测试。一旦测试发现错误,就可以杀死该突变体并尝试其他突变体。


附录:我忘了提到这种效果:Bug容易聚集。这意味着在一个模块中发现的错误越多,发现更多错误的可能性就越高。因此,如果您的测试失败(也就是说,测试是成功的,因为目标是发现错误),则不仅应该修复该错误,还应该使用来为该模块编写更多测试。以上技术。

只要您以稳定的速度发现错误,就必须继续进行测试。仅当发现的新错误率下降时,您才有信心在该开发阶段进行了出色的测试。


7

从一个定义上讲,它更易于维护,因为测试中可能会发现任何重大更改。

但是,代码通过单元测试的事实并不意味着它本质上具有更高的质量。该代码的格式可能仍然不正确,并带有不相关的注释和不适当的数据结构,但仍可以通过测试。

我知道我想维护和扩展哪些代码。


7

完全没有测试的代码可以具有极高的质量,可读性,美观和高效(或完全垃圾),因此,不能说具有80%测试覆盖率的代码比没有测试覆盖率的代码具有更高的质量。

可以公平地说,覆盖80%良好测试的代码可能具有可接受的质量,并且可能相对可维护。但这确实不能保证。



2

我同意可维护部分。迈克尔·费瑟斯(Michael Feathers)最近发布了一段录像,讲述了他关于“ 可测试性和良好设计之间的深层协同作用 ”的精彩演讲,他在其中讨论了这一主题。他在谈话中说,这种关系是一种方式,即设计良好的代码是可测试的,但是可测试的代码不一定是设计良好的。

值得注意的是,视频流在视频中并不是很好,因此,如果您想完整观看,可能值得下载。


-2

一段时间以来,我一直在问自己这个与“条件覆盖”有关的问题。那么来自atollic.com的此页面“为什么进行代码覆盖率分析”呢?

从技术上讲,代码覆盖率分析可以找到程序中未包含在测试用例中的区域,从而使您可以创建其他测试,以覆盖程序中未经测试的部分。因此,重要的是要了解代码覆盖率可以帮助您了解测试过程的质量,而不是代码本身的质量

这似乎在这里很相关。如果您的测试用例集能够达到一定水平的(代码或其他)覆盖率,那么很可能会用相当详尽的一组输入值来调用被测代码!这不会告诉您更多有关被测代码的信息(除非代码崩溃或产生可检测的错误),但是可以使您对测试用例集充满信心

在一个有趣的Necker Cube变更视图中,测试代码现在正在由被测试代码进行测试!


-3

有很多方法可以保证程序能够实现您的预​​期效果,并确保修改不会产生意想不到的效果。

测试是其中之一。避免数据突变是另一种方法。类型系统也是如此。或正式验证。

因此,尽管我同意测试通常是一件好事,但一定比例的测试可能意义不大。我宁愿依赖Haskell编写的未经测试的内容,也不愿依赖经过良好测试的PHP库


这只是您的意见,还是可以通过某种方式进行备份?
gnat 2014年

2
测试不是保证程序达到您想要的目的的一种方法。
Andres F.

1
然后我不知道要进行什么测试
Andrea

@gnat这当然是我的看法。尽管如此,它说了什么。我以Haskell为例,该语言的编译器非常严格,并为输入的格式正确,类型,副作用和数据突变提供了许多保证。我以PHP为例,该语言的解释器非常宽松,甚至没有规范。即使没有测试,类型和效果系统的所有保证也通常会产生相当程度的可靠性。为了通过测试来弥补这一点,您需要拥有一套非常全面的套件
Andrea

我写信时也许有些着急-我正在打电话-但我仍然认为有一点。我不想抨击PHP,但我想说,相比之下,Haskell给出了更高的可靠性是一个客观的陈述
Andrea
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.