參考案例:(本位使用markdown編寫)
git.oschina實例:https://gitee.com/lhhTestTool/LhhMockito
# LhhMockitomock 單元測試 Mockito進行實戰演練# Mockito是什麼?Mockito是mocking框架,它讓你用簡潔的API作測試。並且Mockito簡單易學,它可讀性強和驗證語法簡潔。# Mockito資源官網: http://mockito.orgAPI文檔:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html源碼:https://github.com/mockito/mockito # 使用場景提早建立測試; TDD(測試驅動開發)團隊能夠並行工做你能夠建立一個驗證或者演示程序爲沒法訪問的資源編寫測試Mock 能夠交給用戶隔離系統 # 使用 Mockito 的大體流程建立外部依賴的 Mock 對象, 而後將此 Mock 對象注入到測試類中.執行測試代碼.校驗測試代碼是否執行正確.# 添加maven依賴```xml <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency>```Maven用戶能夠聲明對`mockito-core`的依賴。 Mockito自動發佈到Bintray的中心,並同步到Maven Central Repository。`特別提醒`:使用手工依賴關係管理的Legacy構建可使用`1. *「mockito-all」`分發。 它能夠從Mockito的Bintray存儲庫或Bintray的中心下載。 在可是`Mockito 2. * 「mockito-all」`發行已經中止,Mockito 2以上版本使用`「mockito-core」`。```xml <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.7.19</version> <scope>test</scope> </dependency>```# 添加引用在程序中能夠import org.mockito.Mockito,而後調用它的static方法。`import static org.mockito.Mockito.*;`# 使用介紹## Mock 對象建立 方法`mock(...)`用了 mock 靜態方法來建立一個 Mock 對象. mock 方法接收一個 class 類型, 即咱們須要 mock 的類型```@Testpublic 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 對象後, 能夠開始定製它的具體的行爲.### `when(...).thenReturn(...)` 指定一個返回結果 ,參數匹配```@Testpublic 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!".### `doThrow(ExceptionX).when(x).methodCall` 指定一個拋出異常含義是: 當調用了 `x.methodCall` 方法後, 拋出異常 `ExceptionX`.```@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(new NoSuchElementException()).when(i).next()` 的含義就是: 當第三次調用 `i.next()` 後, 拋出異常 `NoSuchElementException`.(由於 i 這個迭代器只有兩個元素)### `verify()` 校驗 Mock 對象的方法調用Mockito 會追蹤 Mock 對象的所用方法調用和調用方法時所傳遞的參數. 咱們能夠經過 `verify()` 靜態方法來來校驗指定的方法調用是否知足斷言```@Testpublic 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();}```它的含義了, 很簡單: 第一句校驗 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 對象```@Testpublic 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.### `verify(mockedList).addAll(argument.capture()) ` 參數捕獲經過 `verify(mockedList).addAll(argument.capture())` 語句來獲取 `mockedList.addAll` 方法所傳遞的實參 list.```@Testpublic void testCaptureArgument() { List<String> list = Arrays.asList("1", "2"); List mockedList = mock(List.class); ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class); mockedList.addAll(list); verify(mockedList).addAll(argument.capture()); Assert.assertEquals(2, argument.getValue().size()); Assert.assertEquals(list, argument.getValue());}```### RETURNS_SMART_NULLS`RETURNS_SMART_NULLS`實現了Answer接口的對象,它是建立mock對象時的一個可選參數,`mock(Class,Answer)`。在建立mock對象時,有的方法咱們沒有進行stubbing,因此調用時會放回Null這樣在進行操做是極可能拋出NullPointerException。若是經過`RETURNS_SMART_NULLS`參數建立的`mock`對象在沒有調用stubbed方法時會返回SmartNull。例如:返回類型是String,會返回"";是int,會返回0;是List,會返回空的List。另外,在控制檯窗口中能夠看到SmartNull的友好提示。``` @Test public void returnsSmartNullsTest() { List mock = mock(List.class, RETURNS_SMART_NULLS); System.out.println(mock.get(0)); //使用RETURNS_SMART_NULLS參數建立的mock對象,不會拋出NullPointerException異常。另外控制檯窗口會提示信息「SmartNull returned by unstubbed get() method on mock」 System.out.println(mock.toArray().length); }```### RETURNS_DEEP_STUBS`RETURNS_DEEP_STUBS`也是建立mock對象時的備選參數`RETURNS_DEEP_STUBS`參數程序會自動進行mock所需的對象,方法`deepstubsTest`和`deepstubsTest2`是等價的``` @Test public void deepstubsTest(){ Account account=mock(Account.class,RETURNS_DEEP_STUBS); when(account.getRailwayTicket().getDestination()).thenReturn("Beijing"); account.getRailwayTicket().getDestination(); verify(account.getRailwayTicket()).getDestination(); assertEquals("Beijing",account.getRailwayTicket().getDestination()); } @Test public void deepstubsTest2(){ Account account=mock(Account.class); RailwayTicket railwayTicket=mock(RailwayTicket.class); when(account.getRailwayTicket()).thenReturn(railwayTicket); when(railwayTicket.getDestination()).thenReturn("Beijing"); account.getRailwayTicket().getDestination(); verify(account.getRailwayTicket()).getDestination(); assertEquals("Beijing",account.getRailwayTicket().getDestination()); } ```### 模擬方法體拋出異常 `doThrow```` @Test(expected = RuntimeException.class) public void doThrow_when(){ List list = mock(List.class); doThrow(new RuntimeException()).when(list).add(1); list.add(1); }```### 註解使用 `@Mock`測試中咱們在每一個測試方法裏都mock了一個List對象,爲了不重複的mock,是測試類更具備可讀性,咱們可使用下面的註解方式來快速模擬對象:``` @Mock private List mockList;```在使用時必須在基類中添加初始化mock的代碼```public class MockitoExample2 { @Mock private List mockList; public MockitoExample2(){ MockitoAnnotations.initMocks(this); //必須初始化 } @Test public void shorthand(){ mockList.add(1); verify(mockList).add(1); }}```或者使用`built-in runner:MockitoJUnitRunner````@RunWith(MockitoJUnitRunner.class)public class MockitoExample2 { @Mock private List mockList; @Test public void shorthand(){ mockList.add(1); verify(mockList).add(1); }}```不然會提示mock 爲null資料: https://blog.csdn.net/shensky711/article/details/52771493 http://liuzhijun.iteye.com/blog/1512780