android mock测试资源,Android 单元测试 --Mock 及 Mockito
以前我在
Mock 概念
所謂的 Mock 就是創建一個類的虛假的對象,在測試環境中,用來替換掉真實的對象,主要提供兩大功能:
驗證這個對象的某些方法的調用情況,調用了多少次,參數是什么等等
指定這個對象的某些方法的行為,返回特定的值,或者是執行特定的動作
要使用 Mock,一般需要用到 Mock 框架,這篇文章我們使用 Mockito 這個框架,這個是Java界使用最廣泛的一個mock框架。
在 Gradle 添加 Mockito 依賴1
2復制代碼repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }復制代碼
如何使用?
我們首先看一下官方的例子://mock creation List
mockedList = mock(List.class);
//using mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one");
// selective, explicit, highly readable verification
verify(mockedList).add("one");復制代碼
一般使用 Mockito 需要執行下面三步:
模擬并替換測試代碼中外部依賴。
執行測試代碼
驗證測試代碼是否被正確的執行
創建 Mock 對象的方式:
mock(toMockObject.class)
注解的方式 @Mock,注意要利用注解, 首先要告訴 Mockito 框架, 可以@Rule public MockitoRule mockitoRule = MockitoJUnit.rule); 或者它的實現MockitoAnnotations.initMocks(target);
誤區一
上面的例子我們進行修改 ==> 為不同于上面的地方,如下://mock creation List
mock(List.class);
List list = new ArrayList<>();
//using mock object - it does not throw any "unexpected interaction" exception
list.add("one");
// selective, explicit, highly readable verification
verify(list).add("one");復制代碼
運行發現如下錯誤:
org.mockito.exceptions.misusing.NotAMockException:
Argument passed to verify() is of type ArrayList and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
verify(mock).someMethod();
verify(mock, times(10)).someMethod();
verify(mock, atLeastOnce()).someMethod();復制代碼
這就是mock的誤區一:
Mockito.mock()并不是mock一整個類,而是根據傳進去的一個類,mock出屬于這個類的一個對象,并且返回這個mock對象;而傳進去的這個類本身并沒有改變,用這個類new出來的對象也沒有受到任何改變!
結合上面的例子,Mockito.mock(ArrayList.class);只是返回了一個屬于ArrayList這個類的一個mock對象。ArrayList這個類本身沒有受到任何影響,而 list 不是一個mock對象。Mockito.verify()的參數必須是mock對象,也就是說,Mockito只能驗證mock對象的方法調用情況。因此,上面那種寫法就出錯了。
誤區二
我們先看一個例子:public class LoginPresenter {
private UserLoginTask mAuthTask;
public void login(String email, String password) {
mAuthTask = new UserLoginTask(email, password);
//執行登錄操作
mAuthTask.execute();
}
}復制代碼
上面是一個登錄操作, 現在我們來驗證 login() 函數, 因為它沒有返回值,這時候我們只要驗證 execute() 有沒有執行就可以了。
@Test
public void testLogin() throws Exception {
UserLoginTask mockLoginTask = mock(UserLoginTask.class);
LoginPresenter loginPresenter = new LoginPresenter();
loginPresenter.login("jason@gmail.com", "123456");
//驗證是否執行 excute()
verify(mockLoginTask).excute();
}復制代碼
由于 UserLoginTask 繼承 AsyncTask, 所以會報錯,同誤區一中的問題一樣(not mock),這時候我們需要用到 Robolectric 框架,這個可以參考我
mock的誤區二:
mock出來的對象并不會自動替換掉正式代碼里面的對象,你必須要有某種方式把mock對象應用到正式代碼里面。
這個時候我們可以通過 構造方式將依賴傳進去,就 OK 了。
public class LoginPresenter {
private UserLoginTask mAuthTask; //===>
public LoginPresenter(UserLoginTask mAuthTask) {
//TODO test argument
//執行登錄操作
mAuthTask.execute(email, password);
}
}復制代碼
修改測試用例…
LoginPresenter loginPresenter = new LoginPresenter(mockLonginTask); //==>復制代碼
運行發現終于成功了, 不容易。。
驗證方法調用及參數
使用Mockito,驗證一個對象的方法調用情況:
Mockito.verify(objectToVerify).methodToVerify(arguments);
其中 objectToVerify 和 methodToVerify 對應上面的 mockedList 和 add,表示驗證 mockedList 的 add 方法是否傳入參數是 one。
很多時候你并不關心被調用方法的參數具體是什么,或者是你也不知道,你只關心這個方法得到調用了就行。這種情況下,Mockito 提供了一系列的 any 方法,來表示任何的參數都行。
anyString() 表示任何一個字符串都可以。類似 anyString,還有 anyInt, anyLong, anyDouble 等等。anyObject 表示任何對象,any(clazz) 表示任何屬于clazz的對象。 舉個栗子:@Test
public void testDoGet() throws IOException {
Http http = spy(new Http(5000, 5000));
URL url = createUrlConnection();
http.doGet(url, ContentType.HTML.str);
verify(http).createHttpUrlConnection(any(URL.class), any(Http.Method.class), anyString());
}復制代碼
指定 Mock 對象的某些方法的行為
那么接下來,我們就來介紹 Mock 的第二大作用,先介紹其中的第一點:指定 Mock 對象的某個方法返回特定的值。
我們見面的 login() 進行修改, 添加對網絡的判斷, 代碼如下:
public void login(String email, String password) {
//TODO test argument
if(!NetManager.isConnected()) { //添加網絡判斷===>
return;
}
//執行登錄操作
mAuthTask.execute(email, password);
}復制代碼
修改測試代碼:NetManagerWraper netManagerWraper = mock(NetManagerWraper.class); //==>
when(netManagerWraper.isConnected()).thenReturn(false); ==>復制代碼
下面我們說說怎么樣指定一個方法執行特定的動作,這個功能一般是用在目標的方法是 void 類型的時候。
現在假設我們的 LoginPresenter 的 login() 方法是這樣的://執行登錄操作, 并且處理網絡返回
mAuthTask.execute(email, password, new NetworkCallBack() {
@Override
public void onSuccess(Object data) {
}
@Override
public void onFailed(int code, String msg) {
}
});復制代碼
我們想進一步測試傳給 NetworkCallback 里面的代碼,驗證 view 得到了更新等等。在測試環境下,我們并不想依賴 excute 的真實邏輯,而是讓 mAuthTask
直接調用傳入的 NetworkCallback 的 onSuccess 或 onFailed 方法。這種指定 Mock 對象執行特定的動作的寫法如下:
`Mockito.doAnswer(desiredAnswer).when(mockObject).targetMethod(args);
測試代碼如下:
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
//這里可以獲得傳給performLogin的參數
Object[] arguments = invocation.getArguments();
//callback是第三個參數
NetworkCallBack callback = (NetworkCallBack) arguments[2];
callback.onSuccess(null);
return null;
}
}).when(mockLonginTask).execute(anyString(), anyString(), any(NetworkCallBack.class));復制代碼
我們想在調用某些無返回值函數的時候拋出異常,那么可以使用 doThrow 方法。如果想簡單的指定目標方法“什么都不做”,那么可以使用 Mockito.doNothing()。如果你想讓目標方法調用真實的邏輯,可以使用 Mockito.doCallRealMethod()(默認不是的, 請看下文)。
Spy
如果我們不指定 Mock 對象方法的行為, 那么他是不是走真實邏輯呢? 答案是否定的。如果沒我們不指定它的行為,對于 Mock 對象的所有非 void 方法都將返回默認值 int,long 類型方法將返回0,boolean 方法將返回 false,對象方法將返回 null 等等;而 void 方法將什么都不做。
然而很多時候,你希望達到這樣的效果:除非指定,否者調用這個對象的默認實現,同時又能擁有驗證方法調用的功能。這正好是 spy 對象所能實現的效果。
創建Spy方式:
Mockito.spy(toMockObject);
通過注解的方式@Spy
@Test
public void testSpy() {
NetManagerWraper spy = spy(new NetManagerWraper());
assertTrue(spy.isConnected());
when(spy.isConnected()).thenReturn(false);
}復制代碼
spy 與 mock 的唯一區別就是默認行為不一樣: spy 對象的方法默認調用真實的邏輯,mock 對象的方法默認什么都不做,或直接返回默認值。
Mockito Annotation
通過 Mockito 注解可以快速創建 Mock 對象, 這樣對于我們這樣的懶人來說,是不是很爽。 上面我也簡單提到了, 我們可以通過 mock() 和 @Mock 創建, @Mock 就是通過注解的方式創建的, 由于我們使用注解, 當然我們要告訴 Mockito 框架, 不然它怎樣知道你使用注解了, 難道它是神嗎? 加上@Rule 就行了, 這樣JUnit Rule(?)就會每個測試方法測試前進行檢查。添加方法:@Rule public MockitoRule mockitoRule = MockitoJUnit.rule();, 當然創建 spy 對象也可以加 @Spy。
寫在最后
上面的就是 Mockito 的基本使用, 當然由于并不是很全,更全面的可以看
上面代碼在
參考
總結
以上是生活随笔為你收集整理的android mock测试资源,Android 单元测试 --Mock 及 Mockito的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用手机QQ内的扫一扫功能来识别花草
- 下一篇: 路由器和二层交换机之间是如何通信二请问层