<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.8.5</version> <scope>test</scope> </dependency>
然后在程序中直接import static org.mockito.Mockito.*; 即可
List mockList = mock( List.class ); when( mockList .get(0) ).thenReturn( 1 ); assertEquals( "預期返回1", 1, mockList .get( 0 ) );
mockList模擬了List的對象,擁有List的所有方法和屬性。when(...).thenReturn(...)是指當執行這個方法的時候,返回指定的值。相當於模擬配置對象的過程,為某些條件給定一個預期的返回值。
這里的List是Java.util.List接口,並不是實現類,你也可以使用實現類,我們可以使用它們作為打樁對象。這里的打樁(Stub)也可以叫存根,就是把所需的測試數據塞進對象中,關注的是輸入和輸出。這里的when(...).thenReturn(...)就是在定義對象方法和參數(輸入),然后在thenReturn()中指定結果(輸出),這個過程就是Stub打樁,一旦這個方法被Stub了,就會一直返回這個stub的值。當我們連續兩次為同一個方法使用stub的時候,最后的那個stub是有效的。
一旦mock了一個對象之后,mock對象會覆蓋掉整個被mock的對象,因此如果沒有stub方法,就只能返回默認值。當我們mock一個接口時,很多成員方法只是一個簽名,並沒有實現,需要我們手動寫出這些實現方法。比如說,我們模擬request請求對象,被測試的代碼中使用了HttpServletRequest的什么方法,就要寫出相應的實現方法:
HttpServletRequest request = mock(HttpServletRequest.class); when(request.getParameter("foo")).thenReturn("boo");
如果我們不通過when().thenReturn()返回預期值,mockito就會默認返回null,也不會報錯說這個方法找不到。mock實例默認的會給所有的方法添加基本實現:返回null或者空集合,或者0等基本類型的值。
3.迭代風格
// 第一種方式 when(i.next()).thenReturn("Hello").thenReturn("World"); // 第二種方式 when(i.next()).thenReturn("Hello", "World"); // 第三種方式,都是等價的 when(i.next()).thenReturn("Hello"); when(i.next()).thenReturn("World");
4.測試無返回值的方法
doNothing().when(i).remove(); doNothing().when(obj).notify(); // 或直接 when(obj).notify();
5.拋出異常
when(i.next()).thenThrow(new RuntimeException()); doThrow(new RuntimeException()).when(i).remove();
doNothing().doThrow(new RuntimeException()).when(i).remove(); //第一次調用remove方法什么都不做,第二次調用拋出RuntimeException異常。
6.參數匹配器
@Test public void argumentMatchersTest(){ List<String> mock = mock(List.class); when(mock.get(anyInt())).thenReturn("Hello").thenReturn("World"); String result=mock.get(100)+" "+mock.get(200); verify(mock,times(2)).get(anyInt()); assertEquals("Hello World",result); }
@Test public void argumentMatchersTest(){ Map mapMock = mock(Map.class); when(mapMock.put(anyInt(), anyString())).thenReturn("world"); mapMock.put(1, "hello"); verify(mapMock).put(anyInt(), eq("hello")); }
在最后的驗證時如果只輸入字符串”hello”是會報錯的,必須使用Matchers類內建的eq方法。如果將anyInt()換成1進行驗證也需要用eq(1)。
7.驗證Verify
之前的when(...).thenReturn(...)屬於狀態測試,有些時候,測試並不關心返回結果,而是關心方法是否被正確的參數調用過,這時候就應該使用驗證方法了。從概念上講,就是和狀態測試不同的“行為測試”了。一旦通過mock對模擬對象打樁,意味着mockito會記錄着這個模擬對象調用了什么方法,調用了多少次,最后由用戶決定是否需要進行驗證,即verify()方法。
mockedList.add("one"); mockedList.add("two"); verify(mockedList).add("one");
verify 內部跟蹤了所有的方法調用和參數的調用情況,然后會返回一個結果,說明是否通過。
Map mock = Mockito.mock( Map.class ); when( mock.get( "city" ) ).thenReturn( "廣州" ); // 關注參數有否傳入 verify(mock).get( Matchers.eq( "city" ) ); // 關注調用的次數 verify(mock, times( 2 ));
// correct verify(mock).someMethod(anyInt(), anyString(), eq("third argument")); // will throw exception verify(mock).someMethod(anyInt(), anyString(), "third argument");
8.Spy
List spy = spy(new LinkedList()); //Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty) when(spy.get(0)).thenReturn("foo"); //You have to use doReturn() for stubbing doReturn("foo").when(spy).get(0);
當調用when(spy.get(0)).thenReturn("foo")時,會調用真實對象的get(0),由於list是空的所以會拋出IndexOutOfBoundsException異常,用doReturn可以避免這種情況的發生,因為它不會去調用get(0)方法。
9.使用ArgumentCaptor(參數捕獲器) 捕獲方法參數進行驗證
在某些場景中,不光要對方法的返回值和調用進行驗證,同時需要驗證一系列交互后所傳入方法的參數,這時我們可以用參數捕獲器來捕獲傳入方法的參數進行驗證,看它是否符合我們的要求。
@Test public void argumentCaptorTest() { List mock = mock(List.class); List mock2 = mock(List.class); mock.add("John"); mock2.add("Brian"); mock2.add("Jim"); ArgumentCaptor argument = ArgumentCaptor.forClass(String.class); verify(mock).add(argument.capture()); assertEquals("John", argument.getValue()); verify(mock2, times(2)).add(argument.capture()); assertEquals("Jim", argument.getValue()); assertArrayEquals(new Object[]{"Brian","Jim"},argument.getAllValues().toArray()); }