图2 一个有较复杂逻辑的线程
当想对私有方法进行测试时,往往意味着类过于复杂、私有方法承载着太多的职责,通过公有方法覆盖私有方法的测试成本过高,难度太大。因此,更好的解决办法是 分离职责、分而治之、单独测试。通过分离职责,单独对各部分逻辑进行测试,测试就会简单很多,如图3所示;另一个例子如图4所示。
图3 单独对各部分逻辑进行测试
图4 另一个案例
如果在不改变代码的前提下为这样的代码写测试,首先要花很多时间理解它,理清各个分支,分别为它们建立复杂的测试准备状态等,成本很高,有时甚至为不可能完成的任务。
2. Mock是否是遗留系统单元测试的“银弹”:当对遗留系统进行单元测试时,Mock作为“银弹”时不时地出现了。面对遗留系统中有较深依赖链的一些方法, 把那些难以建立的依赖统统Mock掉是最快的手段。但经验告诉我:对Mock保持警惕,一旦引入它,就容易被人滥用。当然它本身无错,错在使用它的人,如 果一定要怪Mock,就怪有些Mock工具过于强大吧。滥用Mock会导致:
我专门回顾了之前做的几个系统,发现用到Mock的时候微乎其微,大多使用在对不受控依赖建立测试替身上。在保证测试执行速度的前提下,这是我推荐的做法。但面对遗留系统时,我们很容易把“难以建立依赖测试状态”作为使用Mock的借口,大量滥用。
单元测试,相对于功能测试等高层次的测试,是代码级别的白盒测试。但之于它的测试对象而言,我们仍然应当把单元测试视为黑盒测试——单元级别的黑盒测试。例 如对一个排序方法而言,不管它采用什么排序算法实现,大多情况下我们只在乎它是否可以将一个无序数组排序。它的测试也只要基于输入(无序数组)和输出判断 (有序数组)即可,就算排序算法改变,测试仍然不受影响。从这个排序方法的角度看,测试对它的内部实现不关心,是黑盒的;当内部实现改变时,只要外部行为 不变,测试就不会受到影响。滥用Mock很容易让测试违反这个特质。
此种情况下,我们选择的方案是:选择从依赖链的末端开始测试,从这里开始逐渐完备测试状态准备机制。在保证测试速度(运行速度和编写速度)的前提下,尽量避免使用Mock。当然,如果你的代码依赖复杂、混乱,那么首先可能要重构,重新组织分配职责,简化依赖关系。
简而言之,面对遗留系统进行单元测试时,有几个值得关注的实践:
之前,我个人对于在遗留系统上实施测试自动化建设是总体悲观的:遗留系统庞大,测试的效果并不会在短期内得以体现;而团队,恰恰经常认为遗留系统足够稳定 (没有什么大需求,而且90%的代码可能短期不需要改动),没有动刀的必要。因此,大多数团队很有可能会在看到测试带来的效果之前就放弃了。
但请相信我,这一切都是假象。故障频发的正是这些遗留系统:
而单元测试,除了它本身带来的测试价值之外,对于遗留系统而言,更大的部分在于以下三个方面:
原文转自:http://www.uml.org.cn/Test/201305025.asp