Mockito官网java
Mockito是一个mock框架。可以帮助咱们使用更加简洁的API,写更漂亮、可读性更强的测试代码。android
mock怎么理解?“模拟”。git
就拿mvp架构来讲。当你想要测试presenter的某个方法时,若是在该方法里,有调用到model层的方法。 而你要根据model层这个方法的返回值,或者产生的反作用,来检测presenter里面的代码。github
这时候,Mockito就能派上用场了。你能够mock这个model的方法,或者返回任意你想要的值,或者让这个方法什么也不作(对无返回值的方法而言),甚至让这个方法抛各类异常,帮助你检测在各类不一样的状况下,你所写的方法,是否都能按你设计的步骤顺利执行。数组
testImplementation "org.mockito:mockito-core:2.27.0"
复制代码
mock(Class<T>
)、spy(Class<T>
)、spy(Object)均会生成一个mock实例。bash
只有mock实例,才能调用Mockito的API来mock各类方法。否则会抛各类异常。架构
注意,文中的“mock实例”,除非特别强调。通常都泛指经由mock(Class)、spy(Class)或spy(Object)方法产生的实例。记笔记~app
MockitoSample mockSample = mock(MockitoSample.class);
MockitoSample spySample = spy(MockitoSample.class);
MockitoSample spyRealSample = spy(new MockitoSample());
复制代码
上述操做中,mock(Class<T>
)、spy(Class<T>
)一般能够简化为下面的形式。但你要mock多个类的时候,较为简便:框架
@Mock
private MockitoSample mockSample;
@Spy
private MockitoSample spySample;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
}
复制代码
<T>
)和spy(Object)其中,由下面的部分Mockito源码可知,spy(Class<T>
)、spy(Object)没有本质的区别。(因此,下文在介绍mock、spy区别的时候,为简便起见。只说spy(Class<T>
)。)ide
public static <T> T spy(Class<T> classToSpy) {
return MOCKITO_CORE.mock(classToSpy, withSettings()
.useConstructor()//该方法返回一个MockSettings类的实例
.defaultAnswer(CALLS_REAL_METHODS));
}
public static <T> T spy(T object) {
return MOCKITO_CORE.mock((Class<T>) object.getClass(), withSettings()
.spiedInstance(object)//该方法也是返回一个MockSettings类的实例
.defaultAnswer(CALLS_REAL_METHODS));
}
复制代码
mock(Class<T>
)、spy(Class<T>
)产生的实例,都能调用Mockito的API来mock方法。
经由mock(Class<T>
)产生的实例,若是没有mock一个方法,就尝试经过该实例调用该方法,不会执行方法的真实逻辑,即不会执行任何操做。若是该方法有返回值,则Object类型、String类型、数组默认返回null,基本类型的数值类型默认返回0、boolean类型默认返回false,集合默认返回空集合,非null。
但经由spy(Class<T>
)产生的实例,若是没有mock一个方法,就尝试经过该实例调用该方法,会执行方法真实逻辑。若是该方法有返回值,则返回真实逻辑执行后产生的值。
验证代码以下:
/**
* publicMethodNoReturnThrowException是一个无返回值、会抛空指针异常的public方法。为了方便
* 理解,文中给出的测试方法,都尽可能遵循这种命名。固然,项目实际运用不会这样命名。
*/
@Test
public void mockClass_notMockPublicMethodNoReturnThrowException(){
mockSample.publicMethodNoReturnThrowException();
}
@Test(expected = NullPointerException.class)
public void spyClass_notMockPublicMethodNoReturnThrowException(){
spySample.publicMethodNoReturnThrowException();
}
复制代码
对于上述知识点的更多验证代码,请参阅文末给出的测试demo。
Mockito提供了不少相似when...thenReturn...
或者doReturn...when...
的方法,都是常见的mock一个方法的手段。
不少时候,这二者是能够互相通用的,你能够选择使用when...thenReturn...
,也能够选择使用doReturn...when...
。如:
String expected = "mockPublicMethodReturnString";
//注意,这里并无真的调用publicMethodReturnString()方法。该代码的含义是当mockSample调用
//publicMethodReturnString()方法时,将会返回你指望的值expected。
when(mockSample.publicMethodReturnString()).thenReturn(expected);
//跟前者等价
doReturn(expected).when(mockSample).publicMethodReturnString();
复制代码
但when...thenReturn...
和doReturn...when...
仍是有很多区别的:
1)when...thenReturn...
更适合咱们的阅读习惯。而doReturn...when...
有点反人类。
2)when...thenReturn...
在mock一个方法时,能进行编译期类型检查。而doReturn...when...
不行。但这并非多重要的特性,由于单元测试运行速度快,doReturn...when...
一运行也能立马检测出来错误。
//传入错误的返回值类型int,编译器将会报错
when(mockSample.publicMethodReturnString()).thenReturn(1);
//传入错误的返回值类型int,编译器不会报错,只有在运行时才能检测到错误
doReturn(1).when(mockSample).publicMethodReturnString();
复制代码
3)when...thenReturn...
没法mock返回值为void的方法。而doReturn...when...
能够。
//publicMethodNoReturnThrowException是一个返回值为void,会抛空指针异常的方法。
//when...thenReturn...没有对应的方法。也不能mock返回值为void的方法。
doNothing().when(mockSample).publicMethodNoReturnThrowException();
//when...thenReturn...有对应的方法。但也不能mock返回值为void的方法。
doThrow(IllegalArgumentException.class).when(mockSample).publicMethodNoReturnThrowException();
复制代码
4)mock、spy产生的mock实例,使用这二者mock方法时,有时会产生不一样的行为:
经由mock(Class<T>
)产生的实例,经过when...thenReturn...
来mock一个方法。而后用该mock实例调用该方法时,在返回指定值以前,不会走真实逻辑。
经由mock(Class<T>
)产生的实例,经过doReturn...when...
来mock一个方法。而后用该mock实例调用该方法时,在返回指定值以前,不会走真实逻辑。
经由spy(Class<T>
)产生的实例,经过when...thenReturn...
来mock一个方法。而后用该mock实例调用该方法时,在返回指定值以前,会走真实逻辑。(这里使用时,须要特别注意。由于这一点,在使用spy(Class<T>
)产生的实例来mock方法的时候,我的不推荐使用when...thenReturn...
,最好使用doReturn...when...
)
经由spy(Class<T>
)产生的实例,经过doReturn...when...
来mock一个方法。而后用该mock实例调用该方法时,在返回指定值以前,不会走真实逻辑。
验证代码,请参阅文末给出的测试demo。
参考资料:Mockito - difference between doReturn() and when()
主要用于验证一个方法被调用过多少次。使用示例代码:
@Test
public void verify_publicMethodReturnString() {
verify(mockSample, never()).publicMethodReturnString();
mockSample.publicMethodReturnString();
//默认状况下是times(1)。times(1)能够被省略。
verify(mockSample).publicMethodReturnString();
mockSample.publicMethodReturnString();
verify(mockSample, times(2)).publicMethodReturnString();
}
复制代码
主要用于配合verify方法,验证方法的调用。使用示例代码:
@Test
public void verify_publicMethodCalculate() {
//能够不使用参数匹配器。
verify(mockSample, never()).publicMethodCalculate(1, 2);
//若是你使用参数匹配器(isA(Class<T>)、anyXxx()、eq()),全部的参数都必须由匹配器提供。。
verify(mockSample, never()).publicMethodCalculate(isA(int.class), isA(int.class));
verify(mockSample, never()).publicMethodCalculate(anyInt(), anyInt());
verify(mockSample, never()).publicMethodCalculate(eq(1), eq(2));
mockSample.publicMethodCalculate(1, 2);
verify(mockSample).publicMethodCalculate(1, 2);
verify(mockSample).publicMethodCalculate(isA(int.class), isA(int.class));
verify(mockSample).publicMethodCalculate(anyInt(), anyInt());
verify(mockSample).publicMethodCalculate(eq(1), eq(2));
verify(mockSample, never()).publicMethodCalculate(1, 1);
verify(mockSample, never()).publicMethodCalculate(eq(1), eq(1));
}
复制代码
看前面看得一脸懵逼?不要紧,来实战一下吧~
下面演示如何测试常见的mvp代码presenter里面的一个loadData()方法。
该Presenter:
public class MainPresenter implements MainContract.Presenter {
private MainContract.View view;
private TestDataSource testDataSource;
public MainPresenter(TestDataSource testDataSource) {
this.testDataSource = testDataSource;
}
@Override
public void attachView(MainContract.View view) {
this.view = view;
}
@Override
public void loadData() {
getDataAndHandle();
}
private void getDataAndHandle() {
//省略一大堆的处理逻辑......
testDataSource.getData()(new TestDataSource.GetDataCallback() {
@Override
public void onSuccess(List<Person> peoples) {
//省略一大堆的处理逻辑......
if (view != null) view.showPersons(peoples);
}
@Override
public void onError() {
//省略一大堆的处理逻辑......
if (view != null) view.showNotDataToast();
}
});
}
}
复制代码
注意:
为了便于咱们测试,presenter的设计也很讲究。这里使用到了依赖注入的思想。model层的TestDataSource的实例,是在presenter的构造方法调用以前就建立好,再传进presenter里面的。这就是一个最简单的依赖注入实现。而Dagger2这些依赖注入框架,只是简化咱们手动一个个去new要注入的实例的繁琐步骤。
为何要使用依赖注入?当咱们要测试loadData()方法时,咱们要使用Mockito控制testDataSource的getData(TestDataSource.GetDataCallback)方法,按照咱们的意愿返回。但前面也说了,“只有mock实例,才能调用Mockito的API来mock各类方法”。若是你的TestDataSource实例是在presenter的构造方法里面建立的。那么你怎么用你的mock实例替换它?诚然,你能够暴露一对set/get方法,用来替换原来代码中的testDataSource,但该set/get方法仅是为了测试而妥协,并无别的实际用处。若是你有多个要mock的类,那岂不是要多写一堆set/get方法?
但若是你使用依赖注入,就能够避免这种尴尬。若是咱们一开始,传入presenter的就是一个mock实例,那么一切迎刃而解。
还有,须要注意的是,若是你使用了Dagger2,在写测试代码时,不建议使用Dagger2建立这个Presenter。直接像下面代码同样,new一个Presenter就行了。
presenter代码:
private void getDataAndHandle() {
testRepository.getData(new GetDataCallback() {
@Override
public void onSuccess(List<Person> peoples) {
if (view != null) view.showPersons(peoples);
}
@Override
public void onError() {
if (view != null) view.showNotDataToast();
}
});
}
复制代码
测试代码:
public class MainPresenterTest {
@Mock
private TestDataSource testDataSource;
@Mock
private MainContract.View view;
private MainPresenter mainPresenter;
/**
* {@link ArgumentCaptor}Captor是一个功能强大的Mockito API,用于捕获参数值并使用它们,
* 对它们进行进一步的行动或断言。但我在本身的项目里,相比回调,更多的时候,用的都是
* RxJava来获取model层的数据。好处是,不用声明各类回调接口,并且RxJava在设计的时候,就考
* 虑到了测试的问题,更易于写测试代码。
*/
@Captor
private ArgumentCaptor<TestDataSource.GetDataCallback> getDataCallbackCaptor;
private List<Person> peoples;
@Before
public void setup() {
//快速mock多个类
MockitoAnnotations.initMocks(this);
//注意,传入的是一个已经经由mock(Class`<T>`)产生的实例
mainPresenter = new MainPresenter(testDataSource);
mainPresenter.attachView(view);
peoples = Arrays.asList(new Person("Tony"), new Person("Alice"));
}
@Test
public void loadData() {
mainPresenter.loadData();
verify(testDataSource, times(1)).getData(getDataCallbackCaptor.capture());
//验证获取数据成功后的逻辑是否顺利完成
getDataCallbackCaptor.getValue().onSuccess(peoples);
verify(view, times(1)).showPersons(peoples);
//验证获取数据失败后的逻辑是否顺利完成
getDataCallbackCaptor.getValue().onError();
verify(view, times(1)).showNotDataToast();
}
}
复制代码
更多的测试Presenter的示例代码,能够参考谷歌的android-architecture几个分支里面的测试代码,好比:
1)todo-mvp:presenter层使用回调获取model层的数据。
2)todo-mvp-rxjava:presenter层使用rxjava获取model层的数据两个分支。
mock、spy在实际运用时,该作何选择?
简单归纳为:
这时使用mock(Class<T>
)。好比,测试presenter,咱们要mock掉model层的方法,通常都是使用mock(Class<T>
)。但有人会说,若是我只想mock掉model层的部分方法,一些方法仍是让它走真实逻辑呢?通常来讲,不会这样子作,毕竟咱们如今要测试的是presenter的方法,应该排除model的干扰,mock掉model层的方法。
这时使用spy(Class<T>
)。好比,现实项目中,我有一个Printer类,大体代码以下(固然,项目里的代码比这还复杂得多):
public void print(String filePath, Callback callback) {
if (getFormat(filePath).equals("pdf")) {
String newFilePath = transform(filePath);
jumpToPrinterShare(newFilePath, callback);
} else {
jumpToPrinterShare(filePath, callback);
}
}
复制代码
当我想测试print方法在传入不一样类型的文件时,可否顺利跳转到PrinterShare,遇到了点小问题。 因为打印机软件PrinterShare对含中文字符的pdf的渲染很差,因此要用第三方框架,把pdf转成图片再打印,也就是这个transform方法。因为它过于复杂,又涉及到第三方库,可能会影响到咱们的测试,因此须要mock该方法。这时,就须要使用spy。注意,这里使用spy(Class<T>
)时,传入的是Printer这个类。而后使用mock实例,调用Mockito的相应API来mock掉transform方法。最后用mock实例,直接调用print方法。这样print方法会走真实逻辑,但若是执行到调用transform方法的地方,不会真的执行此方法,而是直接用你mock的值,继续往下执行剩余逻辑。
测试代码:
public class PrinterTest {
@Spy
private Printer printer;
@Mock
Callback callback;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testPrinterPDFSuccess() {
doReturn("D:\\demo\\a.jpg").when(printer).transform(anyString());
printer.print("D:\\demo\\a.pdf", callback);
verify(callback).onSucess("jpg");
}
}
复制代码
Mockito主要用于单元测试上。使用时,也须要注意一下代码的设计结构,方便测试。
另外,Mockito是不能mock私有方法、静态方法的。2.1.0版本之前的Mockito是不能mock final类和final方法的,以后的也要经过配置一些相关文件才行(Mock the unmockable: opt-in mocking of final classes/methods)。所以,它的补充框架PowerMock也应运而生。(有时候,2.1.0之后的Mockito,采用上述配置文件也未必能mock final类和final方法,跟你的java版本有关)
文中的相关测试例子,以及更多的测试例子都可以在UnitTest里面找到。
更多的测试例子,以及相关API的使用方法,请参考Mockito源码里的测试用例。