在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况。但是我相信,你不是为了覆盖率才要求覆盖率的。你需要有意义的覆盖率,以表明你已经很好地测试了该软件。
衡量代码覆盖率相关的问题总是能够引起我的注意。一方面,我经常发现,公司和组织不一定知道他们在测试期间覆盖了多少代码,这确实很令人惊讶!另一方面,对于一些组织来说,代码覆盖率的数字是如此重要,以至于测试的质量和有效性却变得几乎无关紧要了。他们盲目地追逐100%的代码覆盖率,并相信,如果拥有这个数字,该软件将是优秀的,甚至可能是最好的。其实,这样跟不知道你所测试的内容一样危险,实际上可能更危险,因为它可能给你带来错误的安全感。
代码覆盖率可以用来评估软件质量,这是一个很好且有趣的数字,但请务必记住,这是一种手段,而非目的。我们并不是为了覆盖率而要求覆盖率,因为它应该表明我们在测试软件方面做得很好。如果测试本身没有意义,那么再高的覆盖率也并不意味着软件会更好。重要的目标是确保测试每个代码,而不仅仅是执行代码。没有足够的时间和金钱来全面测试所有内容,至少要确保对所有重要的内容都进行了测试。
这就是说,低覆盖率意味着我们可能测试不足,而高覆盖率本身并不一定与高质量相关联——实际情况要复杂得多。
显然,拥有一个愉快的测试环境,使你拥有“足够”的覆盖率,可以放心使用一个良好、稳定、可维护的测试套件来发布软件,该套件具有“足够的测试”,这是最好的状态。但是,实际上,这些覆盖率陷阱仍然很常见。
陷阱1:“我们不知道我们的覆盖率”
不知道自己的代码覆盖率似乎并不合理——市面上的代码覆盖率工具既便宜又丰富。我的一个朋友告诉我,其实开发人员一般都知道自己的代码覆盖率不理想,因此开发人员和测试人员不愿将覆盖率不高的漏洞暴露给管理层。当然我希望这不是普遍的情况。
团队在尝试评估覆盖率时遇到的一个实际问题是该系统过于复杂。当你逐块构建应用程序时,仅知道将覆盖范围计数器放置在何处可能是一项艰巨的任务。我建议,如果实际上很难衡量应用程序的覆盖范围,则应该对架构进行三思。
陷入这种陷阱的第二种方法发生在可能进行大量测试但没有实际覆盖数量的组织中,因为他们没有找到合适的方法来汇总来自不同测试运行的数量。如果你要进行手动测试、功能测试、单元测试和端到端测试,则不能简单地将数字加起来。即使它们各自实现了25%的覆盖率,结合起来也不太可能达到100%。实际上,当您查看结果时,它更可能接近25%,而不是100%。
事实证明,实际上存在一种以有意义的方式一起测量和增加覆盖范围的方法。在Parasoft,利用报告和分析工具Parasoft DTP捕获的大量细粒度数据,可以在这种情况下使用它来提供全面、汇总的代码覆盖率视图。应用程序监视器能够在测试过程中直接从应用程序中收集覆盖率数据,然后将其发送到Parasoft DTP,后者会汇总所有测试实践以及测试团队和测试运行中的覆盖率数据。
如果听起来像是包含了相当大的信息量,那你是对的!DTP提供了一个交互式仪表板,可帮助你浏览此数据并就将测试重点放在哪里做出决策。请参阅下面的示例仪表板:
如果多个测试覆盖了相同的代码,则不会被计算在内,而未经测试的代码部分则可以快速、轻松地看到。这向你显示了应用程序的哪些部分已经过良好的测试,哪些没有经过测试。你可以在免费的白皮书中阅读所有内容。
因此,没有更多的借口不衡量覆盖率。
陷阱2:“覆盖率就是一切!”
人们常常误以为覆盖率就是一切。一旦团队能够衡量覆盖率,领导通常会说“让我们来增加这个数字”。最终,数字本身变得比测试更重要。正如Parasoft的创始人Adam Kolawa所说的:
“这就像要求钢琴家按照100%的覆盖率敲击钢琴琴键,而不是根据乐谱的需要仅敲击那些有意义的琴键。当他演奏作品时,他会获得有意义的任何数量的按键覆盖。”
问题就出在这里——盲目的要求覆盖率与盲目的音乐演奏相同。覆盖率需要反映出对代码的真实、有意义的使用,否则只会是“噪音”。
说到“噪音”……覆盖率的成本会随着覆盖率的增加而增加。请记住,你不仅需要创建测试,而且还必须维护它们。如果你不打算重复使用和维护测试,则可能一开始就不要浪费时间来创建它。随着测试套件的变大,“噪音”量也会以意想不到的方式增加。两倍的测试可能意味着两倍甚至三倍的“噪音”。无意义的测试最终会比好的测试产生更多的“噪音”,因为它们没有真实的上下文,但是每次执行测试时都必须加以处理。想一想技术债务吧!无用的测试是真正的危险。
现在,在某些行业(例如对安全至关重要的行业)中,必须达到100%的覆盖率指标。但是即使在这种情况下,将一行代码的任何执行都视为有意义的测试也太容易了,这根本不是事实。我要问两个基本问题,以确定一个测试是否是一个好的测试:
1. 测试失败意味着什么?
2. 测试通过意味着什么?
理想情况下,当测试失败时,我们会知道出了什么问题,如果测试真的很好,它将为我们指明正确的方向进行修复。当测试失败时,很多时候没有人知道为什么,没有人可以复制它,而测试被忽略了。相反,当测试通过时,我们应该能够知道所测试的内容——这意味着某个特定功能或某项功能正常运行。
如果您不能回答其中一个问题,则可能是你的测试有问题。如果你不能回答它们中的任何一个,则测试带来的麻烦可能比它产生的价值更多。
摆脱陷阱的方法首先是要了解覆盖率本身并不是目标。真正的目标是创建有用的有意义的测试。这当然需要时间。在简单的代码编写中,单元测试很简单,但是在复杂的实际应用程序中,这意味着编写存根和模拟并使用框架。这可能会花费很多时间,如果你一直不这样做,很容易忘记所涉及的API的细微差别。即使你认真对待测试,创建真正好的测试所花费的时间也可能比你预期的要长。
为了提高单元测试的效率,Java开发测试工具Parasoft Jtest中有一个针对于此的新技术——单元测试助手。单元测试助手承担着完成正确的模拟和存根的繁琐任务。它也可以帮助你以一种有效的方式来扩展现有测试,以增加覆盖范围——帮助你创建良好的单元测试,并提出建议以提高测试覆盖率和测试质量。
希望你已经了解到覆盖率的重要性,并且提高覆盖率是一个值得实现的目标。但是请记住,单纯的追求百分比并没有编写稳定、可维护和有意义的测试更有价值。