Mockito

来源:互联网 发布:云计算板块股票 编辑:程序博客网 时间:2024/06/08 06:27

迁移到Mockito 2

为了继续改进Mockito并进一步提高单元测试体验,我们希望您升级到2.1.0!Mockito遵循语义版本控制,仅在主版本升级时才包含突破性更改。
在一个库的生命周期中,为了推出一系列改变现有行为甚至改变API的全新功能,突破性变化是必要的。有关新版本的全面指南,包括不兼容的更改,
请参阅“Mockito 2的新功能”Wiki页面。我们希望你喜欢Mockito 2!

Mockito Android支持

使用Mockito版本2.6.1,我们提供“native”Android支持。要启用Android支持,请将“mockito-android”库作为依赖项添加到您的项目中。
此工件发布到同一个Mockito组织,可以按照以下方式导入到Android:

repositories {   jcenter()}dependencies {   testCompile "org.mockito:mockito-core:+"   androidTestCompile "org.mockito:mockito-android:+"}

您可以使用上述“testCompile”范围中的“mockito-core”工件继续在常规虚拟机上运行相同的单元测试。
请注意,由于Android VM的限制,您无法使用Android上的内嵌模拟器。
如果您在Android上遇到mocking问题,请在官方问题跟踪栈上提交问题。提供您正在处理的Android版本和项目的依赖关系。

无配置的内联模拟制作

从版本2.7.6开始,我们提供“mockito-inline”工件,可以在不配置MockMaker扩展文件的情况下启用内联模拟。
要使用它,添加mockito-inline而不是mockito-core工件,如下所示:

 repositories {   jcenter() } dependencies {   testCompile "org.mockito:mockito-inline:+" }

请注意,当将内联模拟功能集成到默认模拟器中时,该工件可能会被废除。

有关内联模拟制作的更多信息,请参见第39节。

我们来验证一些行为!

以下示例mock List,因为大多数人熟悉它的接口(如add(),get(),clear()方法)。

实际上,请不要mock List类。请改用真实的实例代替。

 // 静态导入会使代码更简洁 import static org.mockito.Mockito.*; // 创建mock对象 List mockedList = mock(List.class); // 使用mock对象 mockedList.add("one"); mockedList.clear(); // 验证 verify(mockedList).add("one");  verify(mockedList).clear(); // 验证clear方法执行过一次,等同于: verify(mockedList, times(1)).clear();

一旦创建,模拟器将记住所有的交互。然后,您可以选择性地验证您感兴趣的任何交互。

如何做一些测试桩?

 // 你可以模拟具体的类,而不仅仅是接口 LinkedList mockedList = mock(LinkedList.class); // 测试桩,存根行为 when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException()); // 以下输出“first” System.out.println(mockedList.get(0)); // 以下抛出RuntimeException System.out.println(mockedList.get(1)); // 因为get(999)没有存根,因此以下输出null System.out.println(mockedList.get(999)); // 虽然可以验证一个存根调用,但通常它是多余的 // 如果你的代码关心get(0)返回,那么其他的东西就会中断(通常甚至在执行verify()之前)。 // 如果你的代码不在乎什么get(0)返回,那么它不应该被存根。不相信看这里。 verify(mockedList).get(0);
  • 默认情况下,所有的函数都有返回值,mock函数将根据需要返回null,原始/原始包装值或空集合。例如对于int/Integer为0,对于boolean/Boolean为false;
  • 测试桩函数可以被覆盖:例如,常见的测试桩函数可以用于初始化夹具,但测试方法可以覆盖它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
  • 一旦测试桩函数被调用,该函数将会一致返回固定的值;
  • 最后一次调用测试桩函数有时候极为重要 - 当你用同样的参数多次调用相同的方法,最后一次调用可能是你所感兴趣的。也就是说:桩的顺序是重要的,但它很少有意义,例如当使用完全相同的方法调用或有时候使用参数匹配器等时。

参数匹配器

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

 // 使用内置的anyInt()参数匹配器 when(mockedList.get(anyInt())).thenReturn("element"); // 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现): when(mockedList.contains(argThat(isValid()))).thenReturn("element"); // 以下输出element System.out.println(mockedList.get(999)); // 您还可以使用参数匹配器验证 verify(mockedList).get(anyInt()); // 参数匹配器也可以写成Java 8 Lambdas verify(mockedList).add(argThat(someString -> someString.length() > 5));

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

要获取有关自定义参数匹配器的信息,请查看javadoc for ArgumentMatcher类。

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

另外,请参阅ArgumentCaptor类的第15节或javadoc。ArgumentCaptor是一个参数匹配器的特殊实现,它捕获参数值以进一步的断言。

参数匹配器的注意点:

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

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

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));// 上述代码是正确的,因为eq()也是一个参数匹配器verify(mock).someMethod(anyInt(), anyString(), "third argument");// 上述代码是错误的,因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此会抛出异常

anyObject(), eq()这样的匹配器函数不会返回匹配器。在内部,它们在堆栈上记录一个匹配器,并返回一个虚拟值(通常为null)。这种实现是由于java编译器强加的静态类型的安全性。
结果是你不能在验证或者测试桩函数方法之外使用anyObject(), eq()方法。

验证确切的调用次数/至少x/从未

 //using mock mockedList.add("once"); mockedList.add("twice"); mockedList.add("twice"); mockedList.add("three times"); mockedList.add("three times"); mockedList.add("three times"); // 下面的两个验证函数效果一样,因为verify默认验证的就是times(1) verify(mockedList).add("once"); verify(mockedList, times(1)).add("once"); // 验证函数具体的执行次数 verify(mockedList, times(2)).add("twice"); verify(mockedList, times(3)).add("three times"); // 使用never()进行验证,never相当于times(0) verify(mockedList, never()).add("never happened"); // 使用atLeast()/atMost()进行验证 verify(mockedList, atLeastOnce()).add("three times"); verify(mockedList, atLeast(2)).add("three times"); verify(mockedList, atMost(5)).add("three times");

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

为返回值为void的函数抛出异常

doThrow(new RuntimeException()).when(mockedList).clear();// 以下抛出RuntimeException:mockedList.clear(); 

关于doThrow()|doAnswer()等函数族的信息请阅读第12章节。

验证顺序

 // A. 必须以特定顺序调用其方法的单一mock List singleMock = mock(List.class); // 使用单一的mock对象 singleMock.add("was added first"); singleMock.add("was added second"); // 为单个模拟创建一个inOrder验证器 InOrder inOrder = inOrder(singleMock); // 确保add函数首先执行的是add("was added first"),然后才是add("was added second") inOrder.verify(singleMock).add("was added first"); inOrder.verify(singleMock).add("was added second"); // B. 必须以特定顺序使用多个mock List firstMock = mock(List.class); List secondMock = mock(List.class); // 使用多个mock对象 firstMock.add("was called first"); secondMock.add("was called second"); // 为这两个Mock对象创建inOrder验证器 InOrder inOrder = inOrder(firstMock, secondMock); // 以下将确保在secondMock之前调用firstMock inOrder.verify(firstMock).add("was called first"); inOrder.verify(secondMock).add("was called second"); // 哦,A B可以随意混合在一起 ```验证执行顺序是非常灵活的-**你不需要一个一个的验证所有交互**,只需要验证你感兴趣的对象即可。 另外,你可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。### 确保交互操作从未发生在mock对象上```java // 使用Mock对象 - 只有 mockOne 被交互 mockOne.add("one"); // 普通验证 verify(mockOne).add("one"); // 验证该方法从未在mock对象中被调用 verify(mockOne, never()).add("two"); // 验证其他mock对象没有互动 verifyZeroInteractions(mockTwo, mockThree);<div class="se-preview-section-delimiter"></div>

查找冗余的调用

 //using mocks mockedList.add("one"); mockedList.add("two"); verify(mockedList).add("one"); // 下面的验证将会失败 verifyNoMoreInteractions(mockedList);<div class="se-preview-section-delimiter"></div>

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

另请参见never() - 它更加明确,并且很好地传达意图。

简化mock对象的创建-@Mock注解

  • 最小化重复的创建代码
  • 使测试类的代码可读性更高
  • 使验证错误更容易阅读,因为字段名称用于标识mock对象
public class ArticleManagerTest {    @Mock private ArticleCalculator calculator;    @Mock private ArticleDatabase database;    @Mock private UserProvider userProvider;    private ArticleManager manager;<div class="se-preview-section-delimiter"></div>

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

 MockitoAnnotations.initMocks(testClass);

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

参考链接: Mockito & Mockito 中文文档

原创粉丝点击