三、关于编写单元测试的一些建议
理论性的探讨已经足够,是时候讨论一些实践层面的问题了。下面是一些能让你的单元测试处于之前所说的坐标轴的左边的建议。
1. 确保每个测试方法与其它所有的测试方法的关系是正交的(相对独立)
1.1 一个测试方法只能用来测试一种行为,也不能把一种行为分散到多个测试方法中,否则如果日后行为发生改变,就需要修改多个地方。
1.2 不要做不必要的断言(Assert),编写测试方法前一定要先搞清楚验证的行为是什么
a)滥用断言不会提高测试覆盖率
b)如果某个行为不属于测试方法验证的目标,就停止对其测试。关于这一点,TDD有个说法为:一个测试方法有且仅有一个断言。
c)要时刻谨记:单元测试的目的是为了验证方法的行为是否符合预期,而不是监视方法在各种情况的行为。
1.3 一次只测试一个代码单元
a) 你设计的软件架构必须支持对每个单元的独立测试(一个类或功能相关的几个类),不然单元测试之间会有重叠,在这种情况下,某处测试代码的微小改动可能造成数目庞大的级联修改。如果你的软件架构做不到支持单元测试,那么软件质量就无法得到保证,建议使用Inversion Of Control(控制反转)的思想进行重构。
b) 排除所有对外部服务和状态的依赖
引用外部服务会导致测试重叠,而对外部状态的依赖意味着单元测试在不同的情况会有不同的输出。如果你编写的单元测试必须按照一定的次序运行或者必须在数据库和网络就绪后才能运行,说明你已经走在了错误的道路上。(有时,单元测试代码可能会修改静态变量的值,尽量不要这样做,如果无法避免,至少应在测试结束后将修改过的变量复原)。避免添加前置条件;还要避免在独立的单元测试之前统一执行配置代码(原文为Setup Code),否则我们无法确定每个单元测试依赖的假设是什么,同时这也意味着你的单元测试思路出了问题。
c) 不要对配置代码做单元测试。
我们先来明确配置代码的定义。配置代码不是每个代码单元的公共部分。配置代码是可以复制粘贴的,我个人将ASP.NET MVC 中的 filter也归为配置代码的范畴,像[Authorize]或[RequiresSs]之类的标签则是掺杂在普通代码中的配置代码,对于这些代码,最好使用集成测试的方法,从外部观察它们的行为,使用单元测试是没有意义的,这对你的设计没有帮助,也无法帮您检测缺陷。
2. 采用清晰统一的命名规范
如果你是在测试ProductController控制器中的Purchase方法(Action)在库存为0时的行为,你可能会对应创建一个PurchasingTests类和一个名称为ProductPurchaseAction_IfStockIsZero_RendersOutOfStockView()的单元测试方法,这种命名方法同时给出了主题(ProductController控制器中的Purchase方法)、情景(库存为0)和结果(显示“库存为0”)。我不清楚这种命名方法是否已经有固定的名称,但我知道很多人采用这种命名方法,我们是否可以考虑将其称为”S/S/R”命名规范(Subject/Scenario/Result)...
尽量不要使用一些含混不清的命名,比如Purchase()或者OutOfStock(),这一类方法会很难维护,因为你根本无法搞清楚自己在维护什么。
总之,单元测试可以提升项目质量,这一点是毫无疑问的。但对于许多人声称的“做(单元测试)总比不做(单元测试)好”,我是不同意的,单元测试可能产生极高的价值,却也可能造成极大的负担,这都取决于单元测试的编写质量,取决于单元测试的编写者能否很好地理解单元测试的原则和目标。
如何更好地编写单元测试(下)
发表于:2017-08-06
作者:zhixin9001
来源:
 相关文章
单元测试系列之一开篇 单元测试的实践与思考 如何用 JavaScript 编写你的第一个单元测试 如何写出有效的单元测试 Java单元测试用例的编写,有什么技巧? 单元测试:优雅的Spock框架- 周排行
- 月排行
-   白盒测试怎么测?
-   单元测试系列之一开篇
-   单元测试指南
-   单元测试中捕获异步方法的指定异常
-   C#中单元测试如何部署配置文件?
-   淘系用户平台技术团队单元测试建设
-   使用RazorGenerator对视图View进行单元测试
-   一次单元测试优化的过程总结
-   单元测试系列之一开篇
-   什么是单元测试,和集成测试有什么区别?
-   白盒测试怎么测?
-   Android 单元测试,从小白到入门开始
-   测试驱动开发实践:如何使用 Xunit ...
-   单元测试中捕获异步方法的指定异常