原文鏈接:https://mp.weixin.qq.com/s/rQRO2SaqhQImQlnA5NsEQw
原作者:JAVA極客技術
Hello 大家好,我是阿粉,日常工作中很多時候我們都需要同事間的相互配合協(xié)作完成某些功能,所以我們經(jīng)常會遇到服務或者應用內不同模塊之間要互相依賴的場景。比如下面的場景,serviceA 中的 methodA() 方式依賴 serviceB 中的 methodB() 方法返回操作的結果。那如果我們要對自己的methodA() 方法進行編寫單元測試,還需要等其他同事的methodB() 方法開發(fā)完成才行。那有沒有什么辦法我們可以跳過或者說模擬方法 B 的輸出呢?這就引出了我們今天的主角 Mockito,一個優(yōu)秀的 Mock 測試框架。
我們通過使用 Mock 技術可以讓開發(fā)不停滯,Mock技術的作用是將服務與服務之間的依賴在測試自測階段隔離開,讓開發(fā)人員在自己的應用內部通過模擬的方式把需要依賴外部的接口給構造出來,從而保證不被外界的開發(fā)進度所影響。今天我們要談到的Mockito 就是一個優(yōu)秀的 Mock 框架。
Mockito
Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn’t give you hangover because the tests are very readable and they produce clean verification errors.
Mockito 是一個很好用的模擬框架。它讓您可以使用干凈簡單的 API 編寫漂亮的測試。Mockito 的可讀性非常好,不會讓你感到迷惑,產(chǎn)生的驗證錯誤也很明確。
官網(wǎng)地址:https://site.mockito.org/
中文文檔:https://github.com/hehonghui/mockito-doc-zh#0
測試用例 1
首先在工程的 pom 文件里面加依賴,我們加上 mockito 和junit 的依賴。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
接下來我們編寫一個簡單的測試用例,這里我們通過mock 一個 List 對象,先添加幾個元素,后面驗證添加交互是否與我們預期的一致。
@Test
public void testVerify() throws Exception {
//創(chuàng)建 mock 對象
List mockedList = mock(List.class);
mockedList.add("test1");
mockedList.add("test2");
mockedList.add("test2");
mockedList.clear();
//驗證是否執(zhí)行了一次 add("test1") 操作
verify(mockedList).add("test1");
//同上面驗證是否執(zhí)行了一次 add("test1") 操作,默認就是 time(1)
verify(mockedList, times(1)).add("test1");
//驗證是否執(zhí)行了3次 add("test2") 操作
//verify(mockedList, times(3)).add("test2");
verify(mockedList).clear();
}
上面的測試用例我們運行過后是如下效果,測試用例是通過的。
當我們放開verify(mockedList, times(3)).add("test2"); 這一行代碼進行運行時,我們可以看到測試用例未通過,提示的錯誤是我們預期執(zhí)行 3 次,結果實際只執(zhí)行了 2 次add("test2") 操作。
上面的測試用例是驗證對應方式的執(zhí)行次數(shù)是否和預期一致,除了有準確的次數(shù)之外,還有最多,至少,從未等驗證方式,如下所示:
//精確次數(shù)
verify(mockedList, times(3)).add("test2");
//至少 1次
verify(mockedList, atLeastOnce()).add("test2");
//至少 2 次
verify(mockedList, atLeast(2)).add("test2");
//最多 5 次
verify(mockedList, atMost(5)).add("test2");
測試用例 2
通過設值或者打樁的方式預設參數(shù),如下所示,當執(zhí)行 get(0) 操作時,我們通過 thenReturn()方法返回 hello,當執(zhí)行 get(1)操作時我們拋出空指針異常,運行結果如下圖所示:
@Test
public void testWhen() throws Exception {
LinkedList mockedList = mock(LinkedList.class);
//設置值,通常被稱為打樁
when(mockedList.get(0)).thenReturn("hello");
when(mockedList.get(1)).thenThrow(new NullPointerException());
System.out.println(mockedList.get(0));
//這里會打印 "null" 因為 get(2) 沒有設置
System.out.println(mockedList.get(2));
//這里會拋 exception
System.out.println(mockedList.get(1));
//驗證有沒有執(zhí)行 get(0) 操作
verify(mockedList).get(0);
}
可以看到當我們調用 get(0) 和 get(1) 的時候控制臺成功地拋出了異常。這種方式通常被稱為Stubbing,除了使用 when...thenReturn 方式之外,還有一種形式可以表達,代碼如下:
@Test
public void testDoReturn() throws Exception {
Iterator mockedList = mock(Iterator.class);
doReturn("hello").when(mockedList).next();
Object next = mockedList.next();
System.out.println(next);
doReturn("world").when(mockedList).next();
Object next2 = mockedList.next();
System.out.println(next2);
//上面的過程也可以寫成如下方式
doReturn("test1", "test2").when(mockedList).next();
Object next3 = mockedList.next();
System.out.println(next3);
Object next4 = mockedList.next();
System.out.println(next4);
}
運行結果如下所示,也可以用 doThrow() 方法進行拋異常:
測試用例 3
日常開發(fā)中我們通過要保證方法的時效性,或者說我們要保證我們某個方法必須在多長時間內執(zhí)行完成,這個時候我們也可以通過 mock 的方式來驗證我們的方法是否滿足要求。代碼如下:
@Test
public void testTimeout() throws Exception {
HttpService mock = mock(HttpService.class);
String url = "http://www.xxx.com";
mock.getRequest(url);
verify(mock, timeout(100)).getRequest(url);
//timeout時間后,用自定義的檢驗模式驗證getRequest()
VerificationMode customVer = new VerificationMode() {
@Override
public void verify(VerificationData data) {
}
@Override
public VerificationMode description(String s) {
return null;
}
};
verify(mock, new Timeout(100, customVer)).getRequest(url);
}
Mockito 還有很多 API 可以使用,更多的使用方式,大家可以參考下面這個網(wǎng)站。https://www.tutorialspoint.com/mockito/mockito_timeouts.htm,有更詳細的介紹。