为了继续改进Mockito并进一步提高单元测试体验,我们希望您升级到2.1.0!Mockito遵循语义版本控制,仅在主版本升级时才包含突破性更改。
在一个库的生命周期中,为了推出一系列改变现有行为甚至改变API的全新功能,突破性变化是必要的。有关新版本的全面指南,包括不兼容的更改,
请参阅“Mockito 2的新功能”Wiki页面。我们希望你喜欢Mockito 2!
使用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);
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()
方法。
//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)通常可以省略。
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);
"se-preview-section-delimiter">
//using mocks
mockedList.add("one");
mockedList.add("two");
verify(mockedList).add("one");
// 下面的验证将会失败
verifyNoMoreInteractions(mockedList);
"se-preview-section-delimiter">
一些用户可能会在频繁地使用verifyNoMoreInteractions()
,甚至在每个测试函数中都用。但是verifyNoMoreInteractions()
并不建议在每个测试函数中都使用。
verifyNoMoreInteractions()
在交互测试套件中只是一个便利的验证,它的作用是当你需要验证是否存在冗余调用时。滥用它将导致测试代码的可维护性降低。
你可以阅读这篇文档来了解更多相关信息。
另请参见never()
- 它更加明确,并且很好地传达意图。
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;
private ArticleManager manager;
"se-preview-section-delimiter">
注意: 下面这句代码需要在运行测试函数之前被调用,一般放到测试类的基类或者test runner中:
MockitoAnnotations.initMocks(testClass);
你可以使用内置的runner: MockitoJUnitRunner
或者一个rule : MockitoRule
。
关于mock注解的更多信息可以阅读MockitoAnnotations文档。
…
参考链接: Mockito & Mockito 中文文档