Mockito-如何写出好的测试代码(翻译)

mockito

怎么写出好的测试代码

本文翻译自 mockito 官方github的wiki中,原文链接在此.

为我们的软件编写测试用例是非常好的习惯,但是好的测试用例的编写在今天也是同样的重要.

用以下的原则给你的测试代码多一点点热爱:

1.保证测试代码紧凑且易读

为此,像对你的生产环境代码那样无情的重构你的测试代码.否则,任由测试代码变得臃肿腐烂就像在测试中写恐怖的遗留代码一样.如果测试的代码不能很容易重构,那么你的生产环境的旧代码也一样难以重构.所以,去做一个勇敢的代码重构者.

2.避免重复

如下:测试代码使用了一个和解析器中非常相似的正则表达式来输出结果.
一般来说,你不会想要去将测试代码和被测试的代码使用相同的逻辑.所以重复的使用正则表达式在此处并不是一个好的选择.在这个例子中,想想到底应该如何去测试输入和输出的结果呢?比如,如果这段代码是对参数的模板修改,测试代码就不要对参数进行处理,而直接采用已经处理后的结果进行比较.

// use
Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo("this is 'param1', and this is 'param2'"));

// instead of
Assertions.assertThat(processTemplate("param1", "param2")).isEqualTo(String.format("this is '%s', and this is '%s'", param1, param2));

3.尽量多的覆盖代码的可能路径,特别是错误的路径.

一般这种事对测试驱动开发模式的最好实践.在测试驱动的开发模型中,在设计阶段就可以分辨可能会失败的部分.所以不要害羞于去为一个小东西去写一个简单的测试.你不会知道, 什么时间,什么地点,什么原因,你的这段代码就会被用到或者被修改.
也可以通过变异测试的方式来检验当前测试的有效性,变异测试的工具如PIT.

4.不要去mock非你写的对象

这并不是一条红线,但是越过这条红线可能会出现一系列问题(很大的可能会发生).
TDD即涉及涉及,也涉及测试.当mock一个外部的API是不能被用来驱动涉及的,这个API属于其他人.下面的3个部分可以改变这个API的签名和行为.

  1. 想象一下,如果你mock一个第三方库,当这个三方库进行了一次特殊的升级,三方库的逻辑可能会改变一点点,但是测试用例还是可以工作.所以你就想当然的以为,万事ok,测试检查也一路绿灯,直到你部署到你的软件上,直接boom!
  2. 这也是一个你没有和第三方库完美解耦的标志
  3. 同时,第三方库可能需要复杂的并且繁多的mock来正常运行,这就导致了更多的特殊用例,以及更复杂的代码结构.或者是因为复杂的mock外部代码,测试用例并不能完美的覆盖你的代码.

所以,在创建外部lib或者系统的适配器才是正确的途径,但是你也需要意识到因为更多的底层API,异常会导致的抽象泄漏.为了验证与第三方库的集成,请编写集成测试,并使它们尽可能紧凑和易读。
其他工程师已经写了很多上述情景的惨痛记忆:

  • http://davesquared.net/2011/04/dont-mock-types-you-dont-own.html
  • http://www.markhneedham.com/blog/2009/12/13/tdd-only-mock-types-you-own
  • http://blog.8thlight.com/eric-smith/2011/10/27/thats-not-yours.html
  • http://stackoverflow.com/questions/1906344/should-you-only-mock-types-you-own

5.不要去mock所有东西

如果你mock了所有东西,那么你测试的还是你的生产环境的代码吗?所以,勇敢的不mock一些东西吧.

6.不要去mock value objects

为什么人们想这么做呢?
是因为实例化一个对象太痛苦了吗?这并不是一个合适的原因.
如果某个对象难以实例化,这本身就是一个需要重构这个对象的标志.另外一个另类的方式是给你的对象采用builder模式,有许许多多的工具,包括了很多IDE的插件-Lombok等,通过这些工具,你甚至可以在test路径中采用工厂模式.

abstract class CustomerCreations {
   public static Customer customer_with_a_single_item_in_the_basket() {
	   // long init sequence
   }
}

Mockito就是专注于对象之间的互动.这也是面向对象编程最重要的理念吧.

读 <测试驱动的面向对象软件开发>

这本是必读书目.它从0开始了一个feature满满的应用.书的作者解释了软件开发的各个角度,以及在软件开发的生命周期中怎么样在多个场景中使用测试.

如果你有哪些地方不明白,这个邮件列表包含了一大群聪明的家伙.