软件测试的常见实践
虽然每个人都同意测试对于软件项目来说很重要,但就每个人自己处理测试的方式而言仍有一些极具争议的问题。这些开放性的问题牵涉到测试的角色,什么时候写,测试的数量和位置等。
测试驱动开发(TDD)—先写测试
测试驱动开发(TDD)是一个引导开发者从测试开始写代码的流程。
对于每个类或方法而言TDD开发者从一个代表期望行为的测试开始,这个测试最初是失败的。接着,开发者以迭代的方式向方法或类添加代码,使测试通过,并通过重构改善代码。这个重构包括添加更多功能,如改善设计、改善可读性、注入依赖、遵循某种指导原则等。
事实证明,TDD引出了一些基本事实。
- 测试并非TDD主要目标。恰恰相反,测试的目标是让理想的设计从持续重构的过程中自然浮现。
- 在TDD里,测试的最终目标不是高代码覆盖率,而是更好的设计。
- 持续重构不是可选的。重构相比测试更接近TDD的本质。
如果你想极大地改善代码质量和个人开发者的技能,TDD可能是(考虑所有东西)最好的选择,
因为它给你循序渐进的流程:编写一个失败的测试,添加代码,使测试通过,重构,重复这个过程。因为你的代码必须通过测试,你就需要使用松耦合设计,小心处理依赖,避免静态和全局方法,使类保持精简,总是选择最简单的方案,所有这些都是为了使代码变得更好。
后写测试
因为TDD是一个普通的软件开发流程,所以它不是强制选择,也不是清理代码的唯一安全方式。
你可以使用其他同样有效的技术设计你的代码。然而,当你有了代码,需要测试就变得很合理了。如果你不实施TDD(测试作为流程的一部分),测试就会在某一时刻变成你的终点。而你只在你需要它们时才写,也只为你认为有需要的代码而写。
先写代码后测试:
只是用来验证代码和捕获回归,代表项目工作里的一项额外的工作和成本。就这一点而论,这项工作(和成本)在面临期限紧迫或预算紧张时是可以砍掉的。
在TDD里,测试是软件开发流程不可分割的一部分。如果你先写代码,然后在你需要时才写测试,那么测试和代码就是独立的东西。
最终,测试的目的是验证代码和捕获回归。
这些是现代代码开发的基本方面。
代码覆盖率
一般而言,如果测试覆盖足够的代码,成功的测试就会是衡量质量的好方式;但代码覆盖率和实际软件质量之间并没有证明存在实际关系。
而且达到完全的代码覆盖率实际上是不现实的,甚至是不可能的。
代码覆盖率是一个相对通用的术语,它有不同的计算标准
- 函数覆盖率:衡量程序里的每个函数是否都在某些测试里执行了
- 语句覆盖率:在粒度上更接近源代码的一行
- 判断覆盖率:衡量被计算的分支(如if语句)
- 路径覆盖率:检查通向特定代码的所有可能路径是否都被执行了
代码覆盖率只是一个与特定测量有关的数字。测试的相关性才是真正重要的。盲目提高代码覆盖率,甚至要求开发者达到指定的覆盖率都是无补于事的。一个经过良好测试的应用程序是在重要的场景下有高覆盖率的应用程序。
应该测试哪部分代码——设计领域层的优先
工具库是一个相对静态的东西:要么工作,要么不能。如果那里的函数发现缺陷了,会被修复,仅此而己。如果有一个工具非常通用,可以在各个地方调用,可能想有一些测试来检验它,尤其是边界情况。但是,单元测试可能更具相关性,扩展到代码执行业务逻辑的常见情况和边界情况。
通常可以把它称为业务层,或者叫领域层。应该把测试工作放在涉及应用程序逻辑的关键决定的位置上。
下图展示了典型的用户操作工作流。命令跨越应用程序层,产生一系列对各个后端的调用编排,这些后端包括外部服务、领域模型、领域服务和持久层。在响应产生时,会按照特定格式处理、然后返回用户界面。图中的色差表示测试的优先级,色差最深的拥有最高测试优先级。
领域层是最复杂的部分,也是最受需求波动影响的部分。
因此,这个部分的缺陷最多。表现格式的编排和适配包含更多应用程序的机械部分,它们的行为可以通过很多其他方式观察:代码审查,复审,QA测试和普通调试。
一般而言,应该为这些组件的测试设置优先级:
- 被很多其他组件使用的组件
- 需要深入理解需求的逻辑组件
- 由多个开发者共同拥有的组件
不要盲目地坐下来写测试,也不要强迫你的开发者这样做。拥有大量不可能失败的测试是很愚蠢的。
注意:在写单元测试时,你应该详细了解你要测试的单元的内部。实际上,单元测试是一种白盒测试,与黑盒测试不同,在黑盒测试里,测试者不需要了解内部情况,测试也仅限提供指定输入值和期待输出值。
自动化单元测试的构建——使用Pex插件(白盒)
假设你不是TDD的拥护者。在某一时刻,你可能有一个C#类,但没有为它写单元测试。因为代码是你写的,你是最清楚哪些关键地方需要测试的人。但是,人们总是很容易错失、跳过、低估边界情况,尤其是面对可能很大的代码库。结果,所有测试都可能通过,但代码仍然可能出错。
Microsoft Pex插件
Pex在内部使用静态分析技术理解你的代码逻辑并提出需要有那些相关测试建议。
如果测试项目有参数化的测试,Pex也可以弄清需要传递哪种参数组合才能完全覆盖可能的场景。
如果代码里使用 .NET Code Contracts,Pex会使用那些信息对它提出建议或生成的单元测试进行微调。
Pex是一个革新的白盒测试工具
,它有两种使用方式:协助生成重要的单元测试,以及快速检查你的代码并找到漏洞和疏漏。