以自动化测试撬动遗留系统(3)

发表于:2014-09-28来源:uml.org.cn作者:不详点击数: 标签:以自动化测试
图3 单独对各部分逻辑进行测试 图4 另一个案例 如果在不改变代码的前提下为这样的代码写测试,首先要花很多时间理解它,理清各个分支,分别为它们建

  图3 单独对各部分逻辑进行测试

  图4 另一个案例

  如果在不改变代码的前提下为这样的代码写测试,首先要花很多时间理解它,理清各个分支,分别为它们建立复杂的测试准备状态等,成本很高,有时甚至为不可能完成的任务。

  2. Mock是否是遗留系统单元测试的“银弹”:当对遗留系统进行单元测试时,Mock作为“银弹”时不时地出现了。面对遗留系统中有较深依赖链的一些方法, 把那些难以建立的依赖统统Mock掉是最快的手段。但经验告诉我:对Mock保持警惕,一旦引入它,就容易被人滥用。当然它本身无错,错在使用它的人,如 果一定要怪Mock,就怪有些Mock工具过于强大吧。滥用Mock会导致:

  测试真实性的减弱,降低测试程序作为测试本身的价值;

  滥用Mock的“交互验证(Verification)”,使得测试和实现紧密耦合,降低测试的稳定性。

  我专门回顾了之前做的几个系统,发现用到Mock的时候微乎其微,大多使用在对不受控依赖建立测试替身上。在保证测试执行速度的前提下,这是我推荐的做法。但面对遗留系统时,我们很容易把“难以建立依赖测试状态”作为使用Mock的借口,大量滥用。

  单元测试,相对于功能测试等高层次的测试,是代码级别的白盒测试。但之于它的测试对象而言,我们仍然应当把单元测试视为黑盒测试——单元级别的黑盒测试。例 如对一个排序方法而言,不管它采用什么排序算法实现,大多情况下我们只在乎它是否可以将一个无序数组排序。它的测试也只要基于输入(无序数组)和输出判断 (有序数组)即可,就算排序算法改变,测试仍然不受影响。从这个排序方法的角度看,测试对它的内部实现不关心,是黑盒的;当内部实现改变时,只要外部行为 不变,测试就不会受到影响。滥用Mock很容易让测试违反这个特质。

  此种情况下,我们选择的方案是:选择从依赖链的末端开始测试,从这里开始逐渐完备测试状态准备机制。在保证测试速度(运行速度和编写速度)的前提下,尽量避免使用Mock。当然,如果你的代码依赖复杂、混乱,那么首先可能要重构,重新组织分配职责,简化依赖关系。

  简而言之,面对遗留系统进行单元测试时,有几个值得关注的实践:

  分离职责,分而测之,优于私有方法进行测试;

  逐步完备测试状态准备机制,优于使用Mock。

  之前,我个人对于在遗留系统上实施测试自动化建设是总体悲观的:遗留系统庞大,测试的效果并不会在短期内得以体现;而团队,恰恰经常认为遗留系统足够稳定 (没有什么大需求,而且90%的代码可能短期不需要改动),没有动刀的必要。因此,大多数团队很有可能会在看到测试带来的效果之前就放弃了。

  但请相信我,这一切都是假象。故障频发的正是这些遗留系统:

  稳定是假象,遗留系统里隐藏着很多故障和漏洞,只是暂时没有爆发而已;

  稳定是假象,粗劣的设计让任何一个需求的变化都会像霰弹一样影响整个系统,遗留系统大都是极其脆弱的。

  而单元测试,除了它本身带来的测试价值之外,对于遗留系统而言,更大的部分在于以下三个方面:

  驱动遗留系统的重构,提升架构设计和内部质量(遗留系统虽庞大,但坏味道其实都雷同)。这是对遗留系统而言最关键但一般情况下最没有可能去做的事情;

  暴露并解决系统中的缺陷和漏洞,在这个过程中,我们发现并处理了很多漏洞和缺陷,包括多线程处理上、业务逻辑等;

  更重要的是提升团队的重构和设计能力以及团队质量意识,特别是内部质量。

  总结

  面对遗留系统,选择合适的测试策略,能够让自动化测试的投入在一定时期内看到效果,并且建立可持续进行的机制。同为自动化测试,每种测试在面对遗留系统时遇 到的挑战是不同样的,它们起到的效果也不尽相同。对于遗留系统而言,功能测试是盾,只有它的保护,我们才能放心地对遗留系统动刀;而单元测试是刃,它撬动 的不仅是遗留系统,更是遗留团队。

原文转自:http://www.uml.org.cn/Test/201305025.asp