单元测试
假设对下面系统做单元测试:
cook <- waiter <- customer
来测试一个低阶的模块通常来说是比较容易,比如 cook:
cook <- test driver
测试工程师简单地点了不同的餐,然后检验厨师返回了正确的餐。
但是如果要测一个中间模块,就会比较困难,比如测waitrr,该模块使用了其它模块。一个初级的测试工程师可能会使用我们测试cook模块的方法来测试waiter模块:
cook <- waiter <- test driver
测试驱动程序点了不同的餐,并确保waiter也返回了正确的餐点。不幸地是,这意味着waiter模块可能依赖于cook模块的正确性。如果cook模块有其他测试不友好的特性,比如不确定性的行为(菜单包括厨师不会做的菜),过多的依赖性(厨师在他的整个团队到齐之前不会开始做菜),或者许多的资源(一些菜可能需要昂贵的原材料或者需要数个小时才能完成),这个依赖性会变得更糟。
既然这是一个waiter测试,理想情况下,我们只测试waiter的正确性,而不是cook的。特别地,我们想确保waiter正确地传达了顾客的菜单并且正确返回厨师做的菜给顾客。
单元测试意味着测试单元的独立性,所以一个更好地方法将是隔离在测的模块(waitrr)使用mock技术。
-----------------------
| |
v |
test cook <- waiter <- test driver
这里,test cook与test driver合谋。理想地,在测试下的系统被设置为test cook可以比较容易地被替换掉,在与waiter工作的同时又不改变生产代码(例如不会改变waiter代码)。
Mock对象
现在,test cook可以用三种不同的方法实现:
- 一个fake cook - 一个人假装是一个厨师,通过使用速冻菜和微波炉;
- 一个stub cook - 一个热狗供应商总是给你提供热狗,不管你点什么;
- 一个mock cook - 一个便衣警察根据剧本假装是一个厨师在sting operation;
-----------------------
| |
v |
mock cook <- waiter <- test driver
waiter模块的单元测试的一个主要部分关注在waiter模块如何与cook模块交互。一个基于mock的方法关注在全部指定什么是正确的交互以及当其出错时可以检测到。
mock对象提前知道在测试中可能会发生什么(例如,它的哪一个方法会被调用到等等)以及mock对象知道如何来应对(例如提供什么样的返回值)。mock会指出实际发生的与可能会发生的区别。一个自定义的mock对象可以从头开始被创建对每一个测试用例,到执行期待的行为,但是mocking框架努力允许这样一个行为说明是清晰的,并且很容易在这些测试用例中直接指出来。
围绕着基于mock的对话可能看起来像这样:
test driver对mock cook说:期待一个热狗订单,作为回应给了他一个假的热狗
test driver(假装为顾客)对waiter说:我想要一个热狗
waiter跟mock cook说:给我来一个热狗
mock cook跟waiter说:上菜了,一个热狗已经好了(给了waiter假热狗)
waiter对test driver说:这是你要的热狗(将假热狗给了test driver)
test driver:测试成功
但是因为我们的waiter是一个新手,可能发生下面这样的情况
test driver对mock cook说:期待一个热狗订单,作为回应给了他一个假的热狗
test driver(假装为顾客)对waiter说:我想要一个热狗
waitor跟mock cook说:一个汉堡
mock cook停止测试:我期望被告知一个热狗订单!
test driver注意到这个问题:测试失败 - waitor更改了order
或者
test driver对mock cook说:期待一个热狗订单,作为回应给了他一个假的热狗
test driver(假装为顾客)对waitor说:我想要一个热狗
waitor跟mock cook说:一个热狗
mock cook跟waitor说:上菜了,一个热狗已好(给了waitor假热狗)
waitor跟test driver说:这是你的炸薯条(从其它订单那里拿了炸薯条给了test driver)
test driver注意到这个未期待的炸薯条:测试失败,waitor给了错误的一道菜
在没有对比的基于stub对象的情况下,很难清楚地看清mock对象与stub的区别,但是这个答案已经足够了。
同时也要注意到这是一个非常简单的例子,mocking框架从模块到支持综合的测试,允许对期待的行为有非常复杂的说明。要想获得更多的信息,有许多关于mock对象与mocking框架的资料可供查询。
一篇很好的介绍mock的文章:点击打开链接