Mockito 的使用


轉自:Mockito 中文文檔 ( 2.0.26 beta )

轉自:手把手教你 Mockito 的使用

參數匹配器

Argument Matcher(參數匹配器)

Mockito通過equals()方法,來對方法參數進行驗證。但是有時候我們需要更加靈活的參數需求,比如,匹配任何的String類型的參數等等。參數匹配器就是一個能夠滿足這些需求的工具。
Mockito框架中的Matchers類內建了很多參數匹配器,我們常用的Mockito對象就是繼承自Matchers。比如anyInt()匹配任何int類型的參數,anyString()匹配任何字符串...
復制代碼
@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); 
} 
復制代碼
首先mock了List接口,然后用迭代的方式模擬了get方法的返回值,這里用了anyInt()參數匹配器來匹配任何的int類型的參數。所以當第一次調用get方法時輸入任意參數為100方法返回”Hello”,第二次調用時輸入任意參數200返回值”World”。
這里需要注意:
如果使用了參數匹配器,那么所有的參數需要由匹配器來提供,否則將會報錯。假如我們使用參數匹配器stubbing了mock對象的方法,那么在verify的時候也需要使用它。如:
復制代碼
@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)。

自定義匹配器-ArgumentMatcher抽象類

自定義參數匹配器的時候需要繼承ArgumentMatcher抽象類,它實現了Hamcrest框架的Matcher接口,定義了describeTo方法,所以我們只需要實現matches方法在其中定義規則即可。
下面自定義的參數匹配器是匹配size大小為2的List:

 1 class IsListOfTwoElements extends ArgumentMatcher<List> { 
 2     @ 
 3     public boolean matches(Object list) {  
 4         return ((List) list).size() == 2;  
 5     }  
 6 }  
 7   
 8 @Test  
 9 public void argumentMatchersTest(){  
10     List mock = mock(List.class);  
11     when(mock.addAll(argThat(new IsListOfTwoElements()))).thenReturn(true);  
12        
13     mock.addAll(Arrays.asList("one", "two", "three"));  
14     verify(mock).addAll(argThat(new IsListOfTwoElements()));  
15 }

argThat(Matcher<T> matcher)方法用來應用自定義的規則,可以傳入任何實現Matcher接口的實現類。上例中在stubbing和verify addAll方法時通過argThat(Matcher<T> matcher),傳入了自定義的參數匹配器IsListOfTwoElements用來匹配size大小為2的List。因為例子中傳入List的元素為三個,所以測試將失敗。

較復雜的參數匹配將會降低測試代碼的可讀性。有時實現參數對象的equals()方法是個不錯的選擇(Mockito默認使用equals()方法進行參數匹配),它可以使測試代碼更為整潔。另外,有些場景使用參數捕獲器(ArgumentCaptor)要比自定義參數匹配器更加合適。 

 

如何捕獲 mock 方法的調用參數

Mockito以java代碼風格的形式來驗證參數值 : 即通過使用equals()函數。這也是我們推薦用於參數匹配的方式,因為這樣會使得測試代碼更簡單、簡潔。在某些情況下,當驗證交互之后要檢測真實的參數值時這將變得有用。例如 :

 1 @Test
 2 public void captureNonGenericArgument() {
 3   UserDao userDao = Mockito.mock(UserDao.class);
 4   UserService  userService = new UserService(userDao);
 5  
 6   userService.saveUser(new User(null, "Yanbin"));
 7   
 8   ArgumentCaptor<User> argumentCaptor = ArgumentCaptor.forClass(User.class);
 9   verify(userDao, times(1)).save(argumentCaptor.capture());
10  
11    assertEquals("Yanbin", argumentCaptor.getValue().name);
12    assertEquals("Chicago", argumentCator.getValue().city); //可斷言捕獲參數的更多特征

從面對被捕獲參數 argumentCaptor.getValue() 的斷言可看出它比 argThat() 的優勢,argThat() 無法告訴我們不匹配的細節

警告 : 我們建議使用沒有測試樁的ArgumentCaptor來驗證,因為使用含有測試樁的ArgumentCaptor會降低測試代碼的可讀性,因為captor是在斷言代碼塊之外創建的。另一個好處是它可以降低本地化的缺點,因為如果測試樁函數沒有被調用,那么參數就不會被捕獲。總之,ArgumentCaptor與自定義的參數匹配器相關(可以查看ArgumentMatcher類的文檔 )。這兩種技術都能用於檢測外部傳遞到Mock對象的參數。然而,使用ArgumentCaptor在以下的情況下更合適 :

  • 自定義不能被重用的參數匹配器
  • 你僅需要斷言參數值

我們同樣可以在打樁的時候捕獲參數,如

1 ArgumentCaptor<User> argumentCaptor = argumentCaptor.forClass(User.class);
2 when(userDao.findUserLike(argumentCaptor.capture)).thenReturn(Mockito.mock(User.class));
3  
4 assertEquals("Yanbin", argumentCaptor.getValue().name);

 不能以這種方式在打樁的時候捕獲參數:

1 when(userDao.findUserLike(argumentCaptor.capture)).thenReturn(getUser(argumentCaptor.getValue()));

否則會報錯:

出錯位置在打樁的地方。記住打樁並不等於異步調用,它返回的是個固定值!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM