【发布时间】:2010-09-20 17:38:36
【问题描述】:
如果您(或您的组织)渴望对您的代码进行彻底的单元测试,您如何衡量工作的成功或质量?
- 您是否使用代码覆盖率,您的目标是多少百分比?
- 您是否发现像 TDD 这样的理念比指标有更好的影响?
【问题讨论】:
标签: unit-testing testing tdd
如果您(或您的组织)渴望对您的代码进行彻底的单元测试,您如何衡量工作的成功或质量?
【问题讨论】:
标签: unit-testing testing tdd
我的提示不是确定您是否拥有良好的单元测试本身的方法,而是随着时间的推移发展良好的测试套件的方法。
每当您遇到错误时,无论是在您的开发中还是其他人报告的错误,都要修复两次。您首先创建一个重现问题的单元测试。当你的测试失败时,你就去解决问题。
如果问题一开始就存在,那就是关于代码或域的微妙之处的暗示。为它添加一个测试可以确保它在未来永远不会被重新引入。
这种方法的另一个有趣的方面是,它可以帮助您在真正开始查看代码的复杂性之前从更高的层次理解问题。
另外,对于其他人已经提到的测试覆盖率的价值和缺陷,+1。
【讨论】:
代码覆盖率是一个有用的指标,但应谨慎使用。有些人认为代码覆盖率,特别是覆盖率,有点过于认真,并将其视为良好单元测试的指标。
我的经验告诉我,比试图获得 100% 的覆盖率更重要,这并不容易,人们应该专注于检查关键部分是否被覆盖。但即便如此,您也可能会得到误报。
【讨论】:
我非常支持 TDD,但我不太重视覆盖率统计数据。对我来说,单元测试的成功和有用性是开发团队在一段时间的开发过程中感受到的,因为测试 (a) 预先发现错误,(b) 能够在不回归的情况下进行重构和更改,(c) 帮助充实模块化,解耦设计,(d)等等。
或者,正如 Martin Fowler 所说,支持单元测试和 TDD 的轶事证据是压倒性的,但您无法衡量生产力。在此处阅读有关他的 bliki 的更多信息:http://www.martinfowler.com/bliki/CannotMeasureProductivity.html
【讨论】:
要获得对代码的完全信心,您需要不同级别的测试:单元、集成和功能。我同意上面给出的建议,即测试应该是自动化的(持续集成),并且单元测试应该涵盖具有各种边缘案例数据集的所有分支。代码覆盖工具(例如 Cobertura、Clover、EMMA 等)可以识别分支中的漏洞,但不能识别测试数据集的质量。静态代码分析(例如 FindBugs、PMD、CPD)可以在代码中的问题区域成为问题之前识别它们,并大大有助于促进更好的开发实践。
测试应尝试尽可能多地复制应用程序将在其中运行的整体环境。它应该从最简单的情况(单元)开始到最复杂的情况(功能)。对于 Web 应用程序,必须通过各种浏览器来运行网站的所有用例的自动化流程,因此 SeleniumRC 之类的工具应该包含在您的工具包中。
但是,软件的存在是为了满足业务需求,因此还需要针对需求进行测试。这往往是基于功能(网络)测试的手动过程。本质上,您需要针对规范中的每个要求和相应的功能测试构建一个可追溯性矩阵。在创建功能测试时,它们与一个或多个要求相匹配(例如,以 Fred 身份登录、更新帐户详细信息以获取密码、再次注销)。这解决了可交付成果是否符合业务需求的问题。
总的来说,我会提倡一种基于自动化单元测试(JUnit、nUnit 等)的测试驱动开发方法。对于集成测试,我建议在每次构建时使用一个已知数据集自动填充测试数据库,该数据集说明常见用例,但允许在其上构建其他测试。对于功能测试,您将需要某种用户界面机器人(用于 Web 的 SeleniumRC,用于 Swing 的 Abbot 等)。在构建过程中可以很容易地收集关于每个指标的指标,并显示在 CI 服务器(例如 Hudson)上,供所有开发人员查看。
【讨论】:
如果它可以破坏,则应进行测试。如果可以测试,应该是自动化的。
【讨论】:
如果您衡量测试质量的主要方法是某种自动化指标,那么您已经失败了。
指标可能会产生误导,并且可能会被人玩弄。如果指标是判断质量的主要(或者更糟糕的是,唯一的)手段,那么它们就会被玩弄(可能是无意的)。
例如,代码覆盖率具有很大的误导性,因为 100% 的代码覆盖率远不及完整的测试覆盖率。此外,像“80% 代码覆盖率”这样的数字在没有上下文的情况下同样具有误导性。如果该覆盖位于最复杂的代码位中,并且错过了如此简单的代码,很容易通过肉眼验证,那么这比该覆盖以相反的方式有偏差要好得多。
此外,区分测试的测试域(本质上是功能集)及其质量也很重要。测试质量不是由它测试的数量决定的,就像代码质量不是由功能清单决定的一样。测试质量取决于测试在测试中的工作表现。这实际上很难用自动化指标来总结。
下次你去写单元测试的时候,试试这个实验。看看你可以用多少种不同的方式编写它,使其具有相同的代码覆盖率并测试相同的代码。看看是否有可能编写一个满足这些标准的非常差的测试以及一个非常好的测试。我想你可能会对结果感到惊讶。
最终,经验和判断是无可替代的。一个人的眼睛,希望是几只眼睛,需要查看测试代码并决定它是否好。
【讨论】:
代码覆盖率之于测试就像测试之于编程一样。它只能在出现问题时告诉你,它不能告诉你什么时候一切正常。你应该有 100% 的代码覆盖率甚至更高。代码逻辑分支应使用多个输入值进行测试,充分发挥正常、边缘和极端情况。
【讨论】:
我通常做 TDD,所以我先编写测试,这有助于我了解我希望如何使用这些对象。
然后,当我编写类时,大部分情况下我可以发现常见的陷阱(即我所做的假设,例如变量是特定类型或值范围)以及何时出现这些问题我为该特定案例编写了一个特定的测试。
除此之外,并尽可能提高代码覆盖率(有时不可能达到 100%),您或多或少就完成了。然后,如果将来确实出现任何错误,您只需确保为它编写一个测试用例,首先公开它,并在修复后通过。然后照常修复。
【讨论】:
监控代码覆盖率可能很有用,但与其关注任意目标率(80%、90%、100%?),我发现随着时间的推移瞄准积极趋势很有用。
【讨论】:
我认为单元测试的一些最佳实践是:
除非您开发任务关键型软件,否则不要期望达到 100% 的代码覆盖率。达到这个水平可能会非常昂贵,并且对于大多数项目来说不值得付出努力。
【讨论】:
我尝试使用的另一种技术是将您的代码分成两部分。我最近在博客上写了here。简短的描述是在两组库中维护您的生产代码,其中一组(希望较大的集合)具有 100% 的行覆盖率(或者如果您可以测量它更好),另一组(希望是少量代码)具有0% 覆盖率,是的0% 覆盖率。
您的设计应该允许这种分区。这应该可以很容易地查看未涵盖的代码。随着时间的推移,您可能会想到如何将代码从较小的集合移动到较大的集合。
【讨论】:
变异测试的概念似乎很有希望作为一种衡量(测试?)单元测试质量的方法。突变测试基本上意味着对您的生产代码进行小的“突变”,然后查看是否有任何单元测试失败。小突变通常意味着将and 更改为or 或将< 更改为<=。如果一个或多个单元测试失败,则意味着“突变体”被捕获。如果突变体在您的单元测试套件中幸存下来,则意味着您错过了一次测试。当我对具有 100% 行和分支覆盖率的代码应用突变测试时,它通常会发现一些我错过了测试的地方。
请参阅https://en.wikipedia.org/wiki/Mutation_testing 了解有关概念的说明和工具链接。
【讨论】:
【讨论】:
【讨论】: