RSS

Mock,有人爱它我偏恨

06 Sep

一次又一次地在项目中用Mock写测试,每次都是无奈和不情愿。

对话一

Me: 为什么要用Mock呢?
Dev: 因为客户要求只有Model中的代码才能touch数据库。
Me: 构造数据做其他层次的测试而不是mock,这不可以吗?
Dev: …测试数据构造很麻烦。我们用Mock分离其依赖层次的代码,这样就可以专心写当前方法的测试了。
Me: 依赖的低层次方法全都Mock了理想返回值,接下来直接断言结果就好了?高层次的测试过了但低层的反而没过!
Dev: ……Mock不是保证了各层依赖的方法真的被调用了嘛。
Me: 产品代码里我能看见谁被调用,可代码集成后结果到底对不对呢?集成测试在哪呢,怎么知道产品代码放在一起真能work?
Dev: ………Cucumber测试……
Me: Holy crap!

你应懂得:

  • Mock应该作为分离模块和模块之间的手段,以降低测试的难度,例如Mock数据查询,返回自己构造的测试数据而不是访问模块之外的资源。
  • 模块内的层次之间的关联在测试中未必非要由Mock打断,在一定数据的基础之上,为一个原子功能做跨越一二个类的单元测试,更能从集成的角度证明代码的正确性。
  • 比证明“某些依赖确实被调用到”更有意义的是证明“对给定的输入数据能得出期待的结果”。不要把基于结果断言的测试变成用Mock描述实现过程。
  • 如果已经知道是delegate方法到其他方法上,那就省了Mock的测试吧,就像拿着标准答案答考题,过家家一样毫无意义,除非你不幸要完成变态且愚昧的“100%测试覆盖率”。
  • 如果对“某层次代码能否间接访问到数据库”没有苛刻要求,那么有必要的话,可为每个开发机器配置独立数据库,并区分开test数据库和development数据库,让test数据库只跑测试。这样在写访问数据相关的测试时,就可大胆预置测试数据,之后清除也方便,让测试能够“所见即所得”从而更加真实。

对话二

Me: Mock的代码这么多,还对应着产品代码里的实现细节,这不容易写更不容易看懂啊!
Dev: 这也不算多,构造测试数据才麻烦呢。被测方法里调的其他对象方法你不得不Mock啊,不然会报错。
Me: 但是我要用测试来驱动开发啊,这中间的细节我需要到真正写出产品代码时才能确定,这样的测试也太难写了吧?
Dev: …是有点麻烦,不然你先写好断言部分,产品代码需要时再补上Mock吧。
Me: 这是先开发后补测试啊!
Dev: ……一点一点写的…最终反正是有效的测试啦。
Me: 如果被Mock的方法被更改了实现呢,你能及时发现吗?测试全过了可应用启动却废了,这事发生过吧?
Dev: ………改代码时细心一点,多搜索下检查检查,启动应用看一看……
Me: Holy crap!

你应懂得:

  • Mock在测试中必然会构建假设,通常如“某类的某方法在某条件下调用时应返回某结果”。应该将其控制到所测功能的数据源头处,而不是任其泛滥成为实现过程在测试中的描述。
  • Mock的假设在描述时必然会牵扯到所测方法的实现细节,这在一定程度上为先写测试后写代码增加了难度。
  • 过多地使用Mock会降低测试代码的可读性,更重要的是其可维护性。错误的假设不易分辨,因为真实改变时假设还保持不变,无法通过测试的失败而被发现,而代码检查、集成测试以及人工测试成为了代价昂贵的补偿方法。
 

About Wu Shaobo

@ThoughtWorks
Comments Off on Mock,有人爱它我偏恨

Posted by in Uncategorized

 

Comments are closed.