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