针对单元测试的定义,主要有两种看法:
经典学派。经典学派之所以经典,是因为这原本就是人们做单元测试和测试驱动开发的方式
伦敦学派。伦敦学派扎根于伦敦的编程社区。
单元测试的定义
单元测试有很多定义,但是所有的定义都有三个重要的属性。单元测试是一个自动化测试,并且:
验证一小段代码(或者叫一个单元)
执行速度快
使用隔离的方式进行
而隔离的方式是经典学派和伦敦学派的根本区别所在。(我个人使用经典的方式)
隔离问题,伦敦学派的做法
以隔离的方式验证一段代码(一个单元)意味着什么?
伦敦学派把它描述成将被测试系统(SUT,System Under Test)与它的协作者隔离开来。也就是说,如果一个 class 有依赖项或依赖其它的class,需要把这些依赖项都使用测试替身(test double)来替换掉。
测试替身(test double)是一个外观和行为都与其对应的正式版本类似的对象,但它是一个简化版,它降低了复杂性并有助于测试。
使用测试替身替换依赖项,如下图:
这样做的好处有:
如果测试失败,出问题的代码肯定是 SUT(被测试系统)。其它地方不会出现嫌疑,因为其邻居都被测试替身替换掉了。
它能够拆分对象图,也就是沟通的类之间组成的网络。你可以把被测试类的直接依赖项替换掉,这样就不需要再处理依赖项的依赖项了,从而大大减少单元测试的准备工作。
还有一个小的优点,它允许你引入项目范围内的指南:一次只测试一个类。这样的话就无需考虑代码覆盖率的问题了,因为如果建立一个类,那么就创建它对应的一个测试类。如下图:
如何创建测试替身?
首先看经典学派如何做单元测试:
这里面 Customer 是被测试系统 SUT,而 Store 是它的协作者。
在经典学派里,不使用测试替身替代依赖项。
针对同一个类,伦敦学派是这样做单元测试的:
这里我使用了Moq(https://github.com/moq/moq4)框架。
具体的说,我是用mock 这种东西替代了 Store。(而 mock 是测试替身的一种,现在知道这么多就行)。
这里面 Mock<IStore> 就是 Store 的简化版:
在传入特定参数时,HasEnoughInventory 方法会被指定返回 true 或 false。
此外,也可以得到 RemoveInventory 被调用的次数,并将其用于验证。