Mockito 中文文档 ( 2.0.26 beta )

Mockito库能够Mock对象、验证结果以及打桩(stubbing)。

该文档您也可以通过http://mockito.org获取到。所有文档都保存在javadocs中,因为它能够保证文档与源代码的一致性。这样也能够让离线的用户从IDE直接访问到文档。这样一来也能够激励Mockito开发者在每次写代码、每次提交时更新对应的文档。

目录

  1. 迁移到Mockito 2.0
  2. 验证某些行为
  3. 如何做一些测试桩 (Stub)
  4. 参数匹配器 (matchers)
  5. 验证函数的确切、最少、从未调用次数
  6. 为返回值为void的函数通过Stub抛出异常
  7. 按照顺序验证执行结果
  8. 确保交互(interaction)操作不会执行在mock对象上
  9. 查找冗余的调用
  10. 简化mock对象的创建
  11. 为连续的调用做测试桩 (stub)
  12. 为回调做测试桩
  13. doReturn()、doThrow()、doAnswer()、doNothing()、doCallRealMethod()系列方法的运用
  14. 监控真实对象
  15. 修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )
  16. 为下一步的断言捕获参数 (1.8版本之后)
  17. 真实的局部mocks (1.8版本之后)
  18. 重置mocks对象 (1.8版本之后)
  19. 故障排查与验证框架的使用 (1.8版本之后)
  20. 行为驱动开发的别名 (1.8版本之后)
  21. 序列化mock对象
  22. 新的注解 : @Captor,@Spy,@ InjectMocks (1.8.3版本之后)
  23. 验证超时 (1.8.5版本之后)
  24. 自动初始化被@Spies, @InjectMocks注解的字段以及构造函数注入 (1.9.0版本之后)
  25. 单行测试桩 (1.9.0版本之后)
  26. 验证被忽略的测试桩 (1.9.0版本之后)
  27. mock详情 (1.9.5版本之后)
  28. delegate调用真实的实例 (1.9.5版本之后)
  29. MockMaker API (1.9.5版本之后)
  30. BDD风格的验证 (1.10.0版本之后)
  31. 追踪或者Mock抽象类 (1.10.12版本之后)
  32. Mockito mock对象通过ClassLoader能被序列化/反序列化 (1.10.0版本之后)
  33. deep stubs更好的支持泛型 (1.10.0版本之后)
  34. Mockito JUnit 规则 (1.10.17版本之后)
  35. 开/关插件 (1.10.15版本之后)
  36. 自定义验证失败消息 (2.0.0版本之后)

0. 迁移到Mockito 2.0

为了持续提升Mockito以及更进一步的提升单元测试体验,我们希望你升级到Mockito 2.0.Mockito遵循语意化的版本控制,除非有非常大的改变才会变化主版本号。在一个库的生命周期中,为了引入一系列有用的特性,修改已存在的行为或者API等重大变更是在所难免的。因此,我们希望你能够爱上 Mockito 2.0!

重要变更 :

  • Mockito从Hamcrest中解耦,自定义的matchers API也发生了改变,查看ArgumentMatcher 的基本原理以及迁移指南。

跟着我们的示例来mock 一个List,因为大家都知道它的接口(例如add(),get(), clear())。不要mock一个真实的List类型,使用一个真实的实例来替代。

1. 验证某些行为

一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可能选择性的验证你感兴趣的交互。

2. 如何做一些测试桩 (Stub)

  • 默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean;
  • 测试桩函数可以被覆写 : 例如常见的测试桩函数可以用于初始化夹具,但是测试函数能够覆写它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
  • 一旦测试桩函数被调用,该函数将会一致返回固定的值;
  • 上一次调用测试桩函数有时候极为重要-当你调用一个函数很多次时,最后一次调用可能是你所感兴趣的。

3. 参数匹配器 (matchers)

Mockito以自然的java风格来验证参数值: 使用equals()函数。有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :

参数匹配器使验证和测试桩变得更灵活。点击这里查看更多内置的匹配器以及自定义参数匹配器或者hamcrest 匹配器的示例。

如果仅仅是获取自定义参数匹配器的信息,查看ArgumentMatcher类文档即可。

为了合理的使用复杂的参数匹配,使用equals()与anyX() 的匹配器会使得测试代码更简洁、简单。有时,会迫使你重构代码以使用equals()匹配或者实现equals()函数来帮助你进行测试。

同时建议你阅读第15章节或者ArgumentCaptor类文档。ArgumentCaptor是一个能够捕获参数值的特俗参数匹配器。

参数匹配器的注意点 :

如果你使用参数匹配器,所有参数都必须由匹配器提供。

示例 : ( 该示例展示了如何多次应用于测试桩函数的验证 )

像anyObject(), eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中,并且返回一个假的值,通常为null。这样的实现是由于被Java编译器强加的静态类型安全。结果就是你不能在验证或者测试桩函数之外使用anyObject(), eq()函数。

4. 验证函数的确切、最少、从未调用次数

verify函数默认验证的是执行了times(1),也就是某个测试函数是否执行了1次.因此,times(1)通常被省略了。

5. 为返回值为void的函数通过Stub抛出异常

关于doThrow|doAnswer 等函数族的信息请阅读第十二章节。

最初,stubVoid(Object) 函数用于为无返回值的函数打桩。现在stubVoid()函数已经过时,doThrow(Throwable)成为了它的继承者。这是为了提升与 doAnswer(Answer) 函数族的可读性与一致性。

6. 验证执行执行顺序

验证执行顺序是非常灵活的-你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。
另外,你可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。

7. 确保交互(interaction)操作不会执行在mock对象上

8. 查找冗余的调用

一些用户可能会在频繁地使用verifyNoMoreInteractions(),甚至在每个测试函数中都用。但是verifyNoMoreInteractions()并不建议在每个测试函数中都使用。verifyNoMoreInteractions()在交互测试套件中只是一个便利的验证,它的作用是当你需要验证是否存在冗余调用时。滥用它将导致测试代码的可维护性降低。你可以阅读这篇文档来了解更多相关信息。

never()是一种更为明显且易于理解的形式。

9. 简化mock对象的创建

  • 最小化重复的创建代码
  • 使测试类的代码可读性更高
  • 使验证错误更易于阅读,因为字段名可用于标识mock对象

注意!下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中:

你可以使用内置的runner: MockitoJUnitRunner 或者一个rule : MockitoRule
关于mock注解的更多信息可以阅读MockitoAnnotations文档

10. 为连续的调用做测试桩 (stub)

有时我们需要为同一个函数调用的不同的返回值或异常做测试桩。典型的运用就是使用mock迭代器。
原始版本的Mockito并没有这个特性,例如,可以使用Iterable或者简单的集合来替换迭代器。这些方法提供了更自然的方式,在一些场景中为连续的调用做测试桩会很有用。示例如下 :

另外,连续调用的另一种更简短的版本 :

11. 为回调做测试桩

Allows stubbing with generic Answer interface.
运行为泛型接口Answer打桩。

在最初的Mockito里也没有这个具有争议性的特性。我们建议使用thenReturn() 或thenThrow()来打桩。这两种方法足够用于测试或者测试驱动开发。

12. doReturn()、doThrow()、doAnswer()、doNothing()、doCallRealMethod()系列方法的运用

通过when(Object)为无返回值的函数打桩有不同的方法,因为编译器不喜欢void函数在括号内…

使用doThrow(Throwable) 替换stubVoid(Object)来为void函数打桩是为了与doAnswer()等函数族保持一致性。

当你想为void函数打桩时使用含有一个exception 参数的doAnswer() :

当你调用doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() 这些函数时可以在适当的位置调用when()函数. 当你需要下面这些功能时这是必须的:

  • 测试void函数
  • 在受监控的对象上测试函数
  • 不知一次的测试为同一个函数,在测试过程中改变mock对象的行为。

但是在调用when()函数时你可以选择是否调用这些上述这些函数。

阅读更多关于这些方法的信息:

13. 监控真实对象

你可以为真实对象创建一个监控(spy)对象。当你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了。尽量少使用spy对象,使用时也需要小心形式,例如spy对象可以用来处理遗留代码。

监控一个真实的对象可以与“局部mock对象”概念结合起来。在1.8之前,mockito的监控功能并不是真正的局部mock对象。原因是我们认为局部mock对象的实现方式并不好,在某些时候我发现一些使用局部mock对象的合法用例。(第三方接口、临时重构遗留代码,完整的文章在这里

理解监控真实对象非常重要!

有时,在监控对象上使用when(Object)来进行打桩是不可能或者不切实际的。因此,当使用监控对象时请考虑doReturn|Answer|Throw()函数族来进行打桩。例如 :

Mockito并不会为真实对象代理函数调用,实际上它会拷贝真实对象。因此如果你保留了真实对象并且与之交互,不要期望从监控对象得到正确的结果。当你在监控对象上调用一个没有被stub的函数时并不会调用真实对象的对应函数,你不会在真实对象上看到任何效果。

因此结论就是 : 当你在监控一个真实对象时,你想在stub这个真实对象的函数,那么就是在自找麻烦。或者你根本不应该验证这些函数。

14. 修改没有测试桩的调用的默认返回值 ( 1.7版本之后 )

你可以指定策略来创建mock对象的返回值。这是一个高级特性,通常来说,你不需要写这样的测试。然后,它对于遗留系统来说是很有用处的。当你不需要为函数调用打桩时你可以指定一个默认的answer。

关于RETURNS_SMART_NULLS更多的信息请查看 :
RETURNS_SMART_NULLS文档

15. 为下一步的断言捕获参数 (1.8版本之后)

Mockito以java代码风格的形式来验证参数值 : 即通过使用equals()函数。这也是我们推荐用于参数匹配的方式,因为这样会使得测试代码更简单、简洁。在某些情况下,当验证交互之后要检测真实的参数值时这将变得有用。例如 :

警告 : 我们建议使用没有测试桩的ArgumentCaptor来验证,因为使用含有测试桩的ArgumentCaptor会降低测试代码的可读性,因为captor是在断言代码块之外创建的。另一个好处是它可以降低本地化的缺点,因为如果测试桩函数没有被调用,那么参数就不会被捕获。总之,ArgumentCaptor与自定义的参数匹配器相关(可以查看ArgumentMatcher类的文档 )。这两种技术都能用于检测外部传递到Mock对象的参数。然而,使用ArgumentCaptor在以下的情况下更合适 :

  • 自定义不能被重用的参数匹配器
  • 你仅需要断言参数值

自定义参数匹配器相关的资料你可以参考ArgumentMatcher文档。