您如何衡量单元测试的质量?

43

如果您(或您的组织)希望彻底进行单元测试,那么如何衡量您的努力的成功或质量呢?

  • 您是否使用代码覆盖率,您的目标百分比是多少?
  • 您是否认为像TDD这样的哲学比度量更具影响力?
14个回答

38

我的建议并不是用来判断你是否有良好的单元测试,而是一种逐步构建出良好测试套件的方法。

每当你遇到一个bug,无论是在开发过程中还是由他人报告,都要进行两次修复。首先创建一个可以重现问题的单元测试,当你得到失败的测试结果时,再去解决问题。

如果问题一开始就存在,它就暗示着代码或领域的细微之处。添加一个测试可以确保它将来永远不会再次出现。

这种方法的另一个有趣的方面是,在实际查看代码的复杂性之前,它将帮助你从更高的层次理解问题。

此外,其他人提到的测试覆盖率的价值和陷阱也值得关注。


1
Joel 把这个描述为"修复两次",我一直很喜欢这种哲学。+1 - vfilby

14

代码覆盖率是一个有用的指标,但需要谨慎使用。有些人过于关注代码覆盖率,特别是被覆盖的百分比,并认为它是衡量好的单元测试的主要指标。

我的经验告诉我,比起试图获得 100% 的覆盖率(这并不容易),人们应该专注于检查是否覆盖了关键部分。但即使这样,你可能也会得到错误的结果。


谢谢。你能解释一下“检查关键部分”的方式吗?这是基于架构、语言、智慧还是其他什么因素推导出来的呢? - user5248982

11

我非常支持TDD,但是我并不太重视测试覆盖率统计数据。对我来说,单元测试的成功和实用性体现在开发团队在开发周期中感受到的几个方面:(a)及早发现错误,(b)使重构和更改不会引入回归问题,(c)帮助完善模块化、解耦的设计,(d)等等。

或者,就像Martin Fowler所说,支持单元测试和TDD的轶事式证据是压倒性的,但你不能衡量生产力。请在他的博客上阅读更多内容:http://www.martinfowler.com/bliki/CannotMeasureProductivity.html


9
为了对代码有充分的信心,您需要进行不同级别的测试:单元测试、集成测试和功能测试。我同意上面给出的建议,即测试应该是自动化的(持续集成),并且单元测试应该覆盖所有分支,使用各种边缘用例数据集。代码覆盖工具(例如Cobertura、Clover、EMMA等)可以识别分支中的漏洞,但无法识别测试数据集的质量问题。静态代码分析工具(如FindBugs、PMD、CPD)可以在问题成为问题之前识别代码中的问题区域,并大大促进更好的开发实践。
测试应该尽可能地复制应用程序将运行的整体环境。它应该从最简单的情况(单元)开始到最复杂的情况(功能)。对于Web应用程序,使用自动化流程通过网站的所有用例以及各种浏览器是必须的,因此像SeleniumRC这样的工具应该在您的工具包中。
然而,软件存在是为了满足业务需求,因此也需要针对需求进行测试。这往往是基于功能(Web)测试的手动过程。基本上,您需要根据规格说明构建每个要求和相应功能测试的可追溯性矩阵。随着功能测试的创建,它们会与一个或多个要求进行匹配(例如,登录为Fred,更新密码的帐户详细信息,然后再次注销)。这解决了交付成果是否符合业务需求的问题。
总体而言,我会提倡一种基于某种自动化单元测试(JUnit、nUnit等)的测试驱动开发方法。对于集成测试,我建议拥有一个测试数据库,在每次构建时自动填充已知数据集,以说明常见用例,但允许其他测试进行构建。对于功能测试,您需要某种用户界面机器人(Web的SeleniumRC,Swing的Abbot等)。可以在构建过程中轻松地收集有关每个指标的信息,并在CI服务器(例如Hudson)上显示给所有开发人员查看。

8

如果它能够破坏,那么它应该被测试。如果它可以被测试,那么它应该被自动化。


6

代码覆盖率对于测试而言就像测试对于编程一样重要。它只能告诉你是否存在问题,但无法告诉你什么时候一切都正常运行。应该达到100%的代码覆盖率或者更高。代码逻辑的分支应该使用多个输入值进行测试,全面检查正常情况、边缘情况和特殊情况。


3
没错。达到 100% 的代码覆盖率并不意味着你已经完成了测试。你的代码仍然可能存在很多问题。 - Bill the Lizard
2
我现在意识到,100%的代码覆盖率并不意味着你已经完成了测试,它只是意味着代码覆盖率已经没有进一步的用处了。这就像编译器说“是的,它可以编译”,你知道这并不是故事的结局。 - quamrana
重要:https://www.martinfowler.com/bliki/TestCoverage.html - user5248982

6
如果你的测试质量主要以某些自动化指标为衡量标准,那么你已经失败了。
指标可能会误导人,也可能被操纵。如果指标是判断质量的主要(或更糟糕的是唯一)手段,它们将被操纵(可能是无意的)。
例如,代码覆盖率深度误导,因为100%的代码覆盖率远不等于完整的测试覆盖率。同样,在没有上下文的情况下,“80%代码覆盖率”这样的数字也是误导性的。如果该覆盖范围在最复杂的代码部分,并且仅错过了那些非常简单易于通过目视验证的代码,则比反向偏差的覆盖范围显着好得多。
此外,重要的是区分测试的测试域(它的功能集)和其质量。测试质量不是由测试的数量决定的,就像代码质量不是由功能清单决定的一样。测试质量取决于测试工作的表现如何。这实际上很难用自动化指标总结出来。
下次你写单元测试时,请尝试进行以下实验。看看有多少种不同的方式可以编写它,使其具有相同的代码覆盖率并测试相同的代码。看看是否有可能编写一个非常差的测试,以满足这些标准,并编写一个非常好的测试。我认为你会对结果感到惊讶。
最终,经验和判断力是无可替代的。需要人眼(希望是多个眼睛)查看测试代码并决定它是否好。

1
很好!但是,为了让我们学习,您能详细说明“测试质量取决于测试的工作表现如何”吗?您该如何做到这一点?策略应该是什么样子的?如何消除“大师1”与“大师2”的观点偏见等?谢谢。 - user5248982

5

我通常进行测试驱动开发,因此我首先编写测试案例,这有助于我看到如何使用对象。

然后,在编写类时,大部分时间我可以发现常见的陷阱(例如,我做出的假设,例如变量是特定类型或特定值范围),当这些情况出现时,我会为该特定情况编写一个具体的测试案例。

除此之外,尽可能覆盖代码(有时无法达到100%),你也基本完成了。然后,如果未来出现任何错误,只需确保首先编写一个暴露它的测试案例,并在修复后通过测试即可。 然后按照正常流程进行修复。


什么情况下无法达到100%的代码覆盖率?这只是时间限制吗? - Bill the Lizard
@Bill,有时模拟所有东西都非常困难,在这些情况下,除了在实际生产环境中测试一些代码片段外,可能无法进行测试。 - Wedge

3
我认为单元测试的最佳实践如下:
  • 它们必须是自包含的,即不需要过多的配置和外部依赖项就能运行。让测试构建自己的依赖项,如文件和网站,这些都是测试运行所需的。
  • 在修复错误之前使用单元测试来重现错误。这有助于防止错误在未来再次出现。
  • 使用代码覆盖率工具来发现任何单元测试都没有覆盖到的关键代码。
  • 将单元测试与夜间构建和发布构建集成。
  • 将测试结果报告和代码覆盖率报告发布到一个Web站点上,团队中的每个人都可以浏览。最好将发布自动化并集成到构建系统中。

除非您开发的是关键任务软件,否则不要期望达到100%的代码覆盖率。达到这一水平的成本非常高,并且对大多数项目来说不值得这样做。


3

监控代码覆盖率是有用的,但是与其专注于任意目标率(80%、90%、100%?),我发现把目光放在随着时间推移而呈现出积极趋势上更为实用。


同意,但这就足够了吗?这个趋势背后有什么?更关键的部分正在进行测试吗?如何“检查”单元测试?有什么想法吗?谢谢! - user5248982

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接