Mockito 一個(gè)優(yōu)秀的 Mock 測(cè)試框架
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
Hello 大家好,我是阿粉,日常工作中很多時(shí)候我們都需要同事間的相互配合協(xié)作完成某些功能,所以我們經(jīng)常會(huì)遇到服務(wù)或者應(yīng)用內(nèi)不同模塊之間要互相依賴的場(chǎng)景。比如下面的場(chǎng)景,serviceA 中的 methodA() 方式依賴 serviceB 中的 methodB() 方法返回操作的結(jié)果。那如果我們要對(duì)自己的methodA() 方法進(jìn)行編寫單元測(cè)試,還需要等其他同事的methodB() 方法開(kāi)發(fā)完成才行。那有沒(méi)有什么辦法我們可以跳過(guò)或者說(shuō)模擬方法 B 的輸出呢?這就引出了我們今天的主角 Mockito,一個(gè)優(yōu)秀的 Mock 測(cè)試框架。
我們通過(guò)使用 Mock 技術(shù)可以讓開(kāi)發(fā)不停滯,Mock技術(shù)的作用是將服務(wù)與服務(wù)之間的依賴在測(cè)試自測(cè)階段隔離開(kāi),讓開(kāi)發(fā)人員在自己的應(yīng)用內(nèi)部通過(guò)模擬的方式把需要依賴外部的接口給構(gòu)造出來(lái),從而保證不被外界的開(kāi)發(fā)進(jìn)度所影響。今天我們要談到的Mockito 就是一個(gè)優(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 是一個(gè)很好用的模擬框架。它讓您可以使用干凈簡(jiǎn)單的 API 編寫漂亮的測(cè)試。Mockito 的可讀性非常好,不會(huì)讓你感動(dòng)迷惑,產(chǎn)生的驗(yàn)證錯(cuò)誤也很明確。
官網(wǎng)地址:https://site.mockito.org/
中文文檔:https://github.com/hehonghui/mockito-doc-zh#0
測(cè)試用例 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>
接下來(lái)我們編寫一個(gè)簡(jiǎn)單的測(cè)試用例,這里我們通過(guò)mock 一個(gè) List 對(duì)象,先添加幾個(gè)元素,后面驗(yàn)證添加交互是否與我們預(yù)期的一致。
- @Test
- public void testVerify() throws Exception {
- //創(chuàng)建 mock 對(duì)象
- List mockedList = mock(List.class);
- mockedList.add("test1");
- mockedList.add("test2");
- mockedList.add("test2");
- mockedList.clear();
- //驗(yàn)證是否執(zhí)行了一次 add("test1") 操作
- verify(mockedList).add("test1");
- //同上面驗(yàn)證是否執(zhí)行了一次 add("test1") 操作,默認(rèn)就是 time(1)
- verify(mockedList, times(1)).add("test1");
- //驗(yàn)證是否執(zhí)行了3次 add("test2") 操作
- //verify(mockedList, times(3)).add("test2");
- verify(mockedList).clear();
- }
上面的測(cè)試用例我們運(yùn)行過(guò)后是如下效果,測(cè)試用例是通過(guò)的。
當(dāng)我們放開(kāi)verify(mockedList, times(3)).add("test2"); 這一行代碼進(jìn)行運(yùn)行時(shí),我們可以看到測(cè)試用例未通過(guò),提示的錯(cuò)誤是我們預(yù)期執(zhí)行 3 次,結(jié)果實(shí)際只執(zhí)行了 2 次add("test2") 操作。
上面的測(cè)試用例是驗(yàn)證對(duì)應(yīng)方式的執(zhí)行次數(shù)是否和預(yù)期一致,除了有準(zhǔn)確的次數(shù)之外,還有最多,至少,從未等驗(yàn)證方式,如下所示:
- //精確次數(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");
測(cè)試用例 2
通過(guò)設(shè)值或者打樁的方式預(yù)設(shè)參數(shù),如下所示,當(dāng)執(zhí)行 get(0) 操作時(shí),我們通過(guò) thenReturn()方法返回 hello,當(dāng)執(zhí)行 get(1)操作時(shí)我們拋出空指針異常,運(yùn)行結(jié)果如下圖所示:
- @Test
- public void testWhen() throws Exception {
- LinkedList mockedList = mock(LinkedList.class);
- //設(shè)置值,通常被稱為打樁
- when(mockedList.get(0)).thenReturn("hello");
- when(mockedList.get(1)).thenThrow(new NullPointerException());
- System.out.println(mockedList.get(0));
- //這里會(huì)打印 "null" 因?yàn)?nbsp;get(2) 沒(méi)有設(shè)置
- System.out.println(mockedList.get(2));
- //這里會(huì)拋 exception
- System.out.println(mockedList.get(1));
- //驗(yàn)證有沒(méi)有執(zhí)行 get(0) 操作
- verify(mockedList).get(0);
- }
可以看到當(dāng)我們調(diào)用 get(0) 和 get(1) 的時(shí)候控制臺(tái)成功的拋出了異常。這種方式通常被稱為Stubbing,除了使用 when...thenReturn 方式之外,還有一種形式可以表達(dá),代碼如下:
- @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);
- //上面的過(guò)程也可以寫成如下方式
- doReturn("test1", "test2").when(mockedList).next();
- Object next3 = mockedList.next();
- System.out.println(next3);
- Object next4 = mockedList.next();
- System.out.println(next4);
- }
運(yùn)行結(jié)果如下所示,也可以用 doThrow() 方法進(jìn)行拋異常:
測(cè)試用例 3
日常開(kāi)發(fā)中我們通過(guò)要保證方法的時(shí)效性,或者說(shuō)我們要保證我們某個(gè)方法必須在多長(zhǎng)時(shí)間內(nèi)執(zhí)行完成,這個(gè)時(shí)候我們也可以通過(guò) mock 的方式來(lái)驗(yàn)證我們的方法是否滿足要求。代碼如下:
- @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時(shí)間后,用自定義的檢驗(yàn)?zāi)J津?yàn)證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 可以使用,更多的使用方式,大家可以參考這面這個(gè)網(wǎng)站。https://www.tutorialspoint.com/mockito/mockito_timeouts.htm,有更詳細(xì)的介紹。