摘要:什么是是一個強大的用于開發的模擬測試框架通過我們可以創建和配置對象進而簡化有外部依賴的類的測試使用的大致流程如下創建外部依賴的對象然后將此對象注入到測試類中執行測試代碼校驗測試代碼是否執行正確為什么使用我們已經知道了主要的功能就是創建對象那
什么是 Mockito
Mockito 是一個強大的用于 Java 開發的模擬測試框架, 通過 Mockito 我們可以創建和配置 Mock 對象, 進而簡化有外部依賴的類的測試.
使用 Mockito 的大致流程如下:
創建外部依賴的 Mock 對象, 然后將此 Mock 對象注入到測試類中.
執行測試代碼.
校驗測試代碼是否執行正確.
為什么使用 Mockito我們已經知道了 Mockito 主要的功能就是創建 Mock 對象, 那么什么是 Mock 對象呢? 對 Mock 對象不是很了解的朋友, 可以參考這篇文章.
現在我們對 Mock 對象有了一定的了解了, 那么自然就會有人問了, 為什么要使用 Mock 對象? 使用它有什么好處呢?
下面我們以一個簡單的例子來展示一下 Mock 對象到底有什么用.
假設我們正在編寫一個銀行的服務 BankService, 這個服務的依賴關系如下:
當我們需要測試 BankService 服務時, 該真么辦呢?
一種方法是構建真實的 BankDao, DB, AccountService 和 AuthService 實例, 然后注入到 BankService 中.
不用我說, 讀者們也肯定明白, 這是一種既笨重又繁瑣的方法, 完全不符合單元測試的精神. 那么還有一種更加優雅的方法嗎? 自然是有的, 那就是我們今天的主角 Mock Object. 下面來看一下使用 Mock 對象后的框架圖:
我們看到, BankDao, AccountService 和 AuthService 都被我們使用了虛擬的對象(Mock 對象) 來替換了, 因此我們就可以對 BankService 進行測試, 而不需要關注它的復雜的依賴了.
Mockito 基本使用為了簡潔期間, 下面的代碼都省略了靜態導入 import static org.mockito.Mockito.*;
Maven 依賴創建 Mock 對象org.mockito mockito-core 2.0.111-beta
@Test public void createMockObject() { // 使用 mock 靜態方法創建 Mock 對象. List mockedList = mock(List.class); Assert.assertTrue(mockedList instanceof List); // mock 方法不僅可以 Mock 接口類, 還可以 Mock 具體的類型. ArrayList mockedArrayList = mock(ArrayList.class); Assert.assertTrue(mockedArrayList instanceof List); Assert.assertTrue(mockedArrayList instanceof ArrayList); }
如上代碼所示, 我們調用了 mock 靜態方法來創建一個 Mock 對象. mock 方法接收一個 class 類型, 即我們需要 mock 的類型.
配置 Mock 對象當我們有了一個 Mock 對象后, 我們可以定制它的具體的行為. 例如:
@Test public void configMockObject() { List mockedList = mock(List.class); // 我們定制了當調用 mockedList.add("one") 時, 返回 true when(mockedList.add("one")).thenReturn(true); // 當調用 mockedList.size() 時, 返回 1 when(mockedList.size()).thenReturn(1); Assert.assertTrue(mockedList.add("one")); // 因為我們沒有定制 add("two"), 因此返回默認值, 即 false. Assert.assertFalse(mockedList.add("two")); Assert.assertEquals(mockedList.size(), 1); Iterator i = mock(Iterator.class); when(i.next()).thenReturn("Hello,").thenReturn("Mockito!"); String result = i.next() + " " + i.next(); //assert Assert.assertEquals("Hello, Mockito!", result); }
我們使用 when(?...).thenReturn(?...) 方法鏈來定義一個行為, 例如 "when(mockedList.add("one")).thenReturn(true)" 表示: 當調用了mockedList.add("one"), 那么返回 true.. 并且要注意的是, when(?...).thenReturn(?...) 方法鏈不僅僅要匹配方法的調用, 而且要方法的參數一樣才行.
而且有趣的是, when(?...).thenReturn(?...) 方法鏈可以指定多個返回值, 當這樣做后, 如果多次調用指定的方法, 那么這個方法會依次返回這些值. 例如 "when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");", 這句代碼表示: 第一次調用 i.next() 時返回 "Hello,", 第二次調用 i.next() 時返回 "Mockito!".
上面的例子我們展示了方法調用返回值的定制, 那么我們可以指定一個拋出異常嗎? 當然可以的, 例如:
@Test(expected = NoSuchElementException.class) public void testForIOException() throws Exception { Iterator i = mock(Iterator.class); when(i.next()).thenReturn("Hello,").thenReturn("Mockito!"); // 1 String result = i.next() + " " + i.next(); // 2 Assert.assertEquals("Hello, Mockito!", result); doThrow(new NoSuchElementException()).when(i).next(); // 3 i.next(); // 4 }
上面代碼的第一第二步我們已經很熟悉了, 接下來第三部我們使用了一個新語法: doThrow(ExceptionX).when(x).methodCall, 它的含義是: 當調用了 x.methodCall 方法后, 拋出異常 ExceptionX.
因此 doThrow(new NoSuchElementException()).when(i).next() 的含義就是: 當第三次調用 i.next() 后, 拋出異常 NoSuchElementException.(因為 i 這個迭代器只有兩個元素)
Mockito 會追蹤 Mock 對象的所用方法調用和調用方法時所傳遞的參數. 我們可以通過 verify() 靜態方法來來校驗指定的方法調用是否滿足斷言. 語言描述有一點抽象, 下面我們仍然以代碼來說明一下.
@Test public void testVerify() { List mockedList = mock(List.class); mockedList.add("one"); mockedList.add("two"); mockedList.add("three times"); mockedList.add("three times"); mockedList.add("three times"); when(mockedList.size()).thenReturn(5); Assert.assertEquals(mockedList.size(), 5); verify(mockedList, atLeastOnce()).add("one"); verify(mockedList, times(1)).add("two"); verify(mockedList, times(3)).add("three times"); verify(mockedList, never()).isEmpty(); }
上面的例子前半部份沒有什么特別的, 我們關注后面的:
verify(mockedList, atLeastOnce()).add("one"); verify(mockedList, times(1)).add("two"); verify(mockedList, times(3)).add("three times"); verify(mockedList, never()).isEmpty();
讀者根據代碼也應該可以猜測出它的含義了, 很簡單:
第一句校驗 mockedList.add("one") 至少被調用了 1 次(atLeastOnce)
第二句校驗 mockedList.add("two") 被調用了 1 次(times(1))
第三句校驗 mockedList.add("three times") 被調用了 3 次(times(3))
第四句校驗 mockedList.isEmpty() 從未被調用(never)
使用 spy() 部分模擬對象Mockito 提供的 spy 方法可以包裝一個真實的 Java 對象, 并返回一個包裝后的新對象. 若沒有特別配置的話, 對這個新對象的所有方法調用, 都會委派給實際的 Java 對象. 例如:
@Test public void testSpy() { List list = new LinkedList(); List spy = spy(list); // 對 spy.size() 進行定制. when(spy.size()).thenReturn(100); spy.add("one"); spy.add("two"); // 因為我們沒有對 get(0), get(1) 方法進行定制, // 因此這些調用其實是調用的真實對象的方法. Assert.assertEquals(spy.get(0), "one"); Assert.assertEquals(spy.get(1), "two"); Assert.assertEquals(spy.size(), 100); }
這個例子中我們實例化了一個 LinkedList 對象, 然后使用 spy() 方法對 list 對象進行部分模擬. 接著我們使用 when(...).thenReturn(...) 方法鏈來規定 spy.size() 方法返回值是 100. 隨后我們給 spy 添加了兩個元素, 然后再 調用 spy.get(0) 獲取第一個元素.
這里有意思的地方是: 因為我們沒有定制 add("one"), add("two"), get(0), get(1), 因此通過 spy 調用這些方法時, 實際上是委派給 list 對象來調用的.
然而我們 定義了 spy.size() 的返回值, 因此當調用 spy.size() 時, 返回 100.
Mockito 允準我們捕獲一個 Mock 對象的方法調用所傳遞的參數, 例如:
@Test public void testCaptureArgument() { Listlist = Arrays.asList("1", "2"); List mockedList = mock(List.class); ArgumentCaptor argument = ArgumentCaptor.forClass(List.class); mockedList.addAll(list); verify(mockedList).addAll(argument.capture()); Assert.assertEquals(2, argument.getValue().size()); Assert.assertEquals(list, argument.getValue()); }
我們通過 verify(mockedList).addAll(argument.capture()) 語句來獲取 mockedList.addAll 方法所傳遞的實參 list.
本文由 yongshun 發表于個人博客, 采用署名-非商業性使用-相同方式共享 3.0 中國大陸許可協議.
非商業轉載請注明作者及出處. 商業轉載請聯系作者本人
Email: yongshun1228@gmail.com
本文標題為: 手把手教你 Mockito 的使用
本文鏈接為: https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65076.html
摘要:配置配置使用概率抽樣。采樣率定義了對跟蹤跨度進行采樣的概率,其值可以介于和含之間。例如,以下配置對象將采樣率更改為即每個跨度都被采樣,并使用協議將跟蹤發送到位于的服務器文件路徑注將采樣率更改為會完全禁用跟蹤。目錄手把手教你學Dapr - 1. .Net開發者的大時代手把手教你學Dapr - 2. 必須知道的概念手把手教你學Dapr - 3. 使用Dapr運行第一個.Net程序手把手教你學Da...
摘要:和前面幾節課類似的分析這節課就不做了,對于分頁,請求什么的,大家可以直接參考前面的四節課,這一刻主要特別的是,我們在采集商品的同時,會將京東的商品評價采集下來。 系列教程: 手把手教你寫電商爬蟲-第一課 找個軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實戰尚妝網分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實戰尚妝網AJAX請求處理和內容提取 手把手教你寫電商爬蟲-第四課 淘寶網商品爬...
摘要:和前面幾節課類似的分析這節課就不做了,對于分頁,請求什么的,大家可以直接參考前面的四節課,這一刻主要特別的是,我們在采集商品的同時,會將京東的商品評價采集下來。 系列教程: 手把手教你寫電商爬蟲-第一課 找個軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實戰尚妝網分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實戰尚妝網AJAX請求處理和內容提取 手把手教你寫電商爬蟲-第四課 淘寶網商品爬...
摘要:一步一步教你基于搭建自己的個人博客,作為成熟的框架,美觀,方便,插件多,更新頻繁,非常適合個人博客與網站的搭建,適合新手,無需太多的代碼基礎。原文鏈接手把手教你搭建自己的網站購買購買云服務器為了搭建個人網站,首先肯定需要一個云服務器。 一步一步教你基于WordPress搭建自己的個人博客,WordPress作為成熟的CMS框架,美觀,方便,插件多,更新頻繁,非常適合個人博客與網站的搭建...
閱讀 2380·2023-04-25 19:27
閱讀 3492·2021-11-24 09:39
閱讀 3909·2021-10-08 10:17
閱讀 3400·2019-08-30 13:48
閱讀 1934·2019-08-29 12:26
閱讀 3124·2019-08-28 17:52
閱讀 3540·2019-08-26 14:01
閱讀 3537·2019-08-26 12:19