單元測試系列之2:模擬利器Mockito

引述:程序測試對保障應用程序正確 性而言,其重要性怎麼樣強調都不爲過。JUnit是必須事先掌握的測試框架,大多數測試框架和測試工具都在此基礎上擴展而來,Spring對測試所提供的 幫助類也是在JUnit的基礎上進行演化的。直接使用JUnit測試基於Spring的應用存在諸多不便,不可避免地須要將大量的精力用於應付測試夾具準 備、測試現場恢復、訪問測試數據操做結果等邊緣性的工做中。Mockito、Unitils、Dbunit等框架的出現,這些問題有了很好的解決方案,特 別是Unitils結合Dbunit對測試DAO層提供了強大的支持,大大提升了編寫測試用例的效率和質量。
 
    也許在單元測試框架領域,testNG、JUnit的高下上尚有爭論,它們各有本身的擁躉,可是在測試模擬領域,Mockito無疑是翹楚,JMock等只能站後了。

模擬測試概述

   目前支持Java語言的Mock測試工具備EasyMock、JMock、Mockito、MockCreator、Mockrunner、 MockMaker等,Mockito是一個針對Java的Mocking框架。它與EasyMock和JMock很類似,是一套經過簡單的方法對於指定 的接口或類生成 Mock 對象的類庫,避免了手工編寫Mock對象。但Mockito是經過在執行後校驗什麼已經被調用,它消除了對指望行爲(Expectations)的須要。 使用Mockito,在準備階段只需花費不多的時間,可使用簡潔的API編寫出漂亮的測試,能夠對具體的類建立Mock對象,而且有「監視」非Mock 對象的能力。

   Mockito使用起來簡單,學習成本很低,並且具備很是簡潔的API,測試代碼的可讀性很高,所以它十分受歡迎,用戶羣愈來愈多,不少開源軟件也選擇了 Mockito。要想了解更多有關Mockito的信息,能夠訪問其官方網站http://www.mockito.org/。在開始使用Mockito 以前,先簡單瞭解一下Stub和Mock的區別。相比Easymock,JMock,編寫出來的代碼更加容易閱讀。無須錄製mock方法調用就返回默認值 是一個很大優點。目前最新的版本是1.9.0。

   Stub對象用來提供測試時所須要的測試數據,能夠對各類交互設置相應的迴應。例如咱們能夠設置方法調用的返回值等。Mockito中 when(…).thenReturn(…) 這樣的語法即是設置方法調用的返回值。另外也能夠設置方法在什麼時候調用會拋出異常等。

   Mock對象用來驗證測試中所依賴對象間的交互是否可以達到預期。Mockito中用 verify(…).methodXxx(…) 語法來驗證 methodXxx方法是否按照預期進行了調用。有關 stub和mock的詳細論述請見Martin Fowler的文章《Mocks Aren't Stub》,地址爲http://martinfowler.com/articles/mocksArentStubs.html。在Mocking框 架中所謂的Mock對象其實是做爲上述的Stub和Mock對象同時使用的。由於它既能夠設置方法調用返回值,又能夠驗證方法的調用。

建立Mock對象

   能夠對類和接口進行Mock對象的建立,建立的時候能夠爲Mock對象命名,也能夠忽略命名參數。爲Mock對象命名的好處就是調試的時候會很方便。比 如,咱們Mock多個對象,在測試失敗的信息中會把有問題的Mock對象打印出來,有了名字咱們能夠很容易定位和辨認出是哪一個Mock對象出現的問題。另 外它也有限制,對於final類、匿名類和Java的基本類型是沒法進行Mock的。除了用Mock方法來建立模擬對象,如 mock(Class<T> classToMock),也可使用@mock註解定義Mock,下面咱們經過實例來介紹一下如何建立一個Mock對象。
html

Java代碼   收藏代碼
  1. import org.junit.Test;  
  2. import org.mockito.Mock;  
  3. import com.baobaotao.domain.User;  
  4. import com.baobaotao.service.UserService;  
  5. import com.baobaotao.service.UserServiceImpl;  
  6. import static org.junit.Assert.*;  
  7. import static org.mockito.Mockito.*;  
  8. import org.mockito.MockitoAnnotations;  
  9. …  
  10. public class MockitoSampleTest{  
  11.        
  12.     //① 對接口進行模擬  
  13.     UserService mockUserService = mock(UserService.class);  
  14.     //② 對類進行模擬  
  15.     UserServiceImpl mockServiceImpl = mock(UserServiceImpl.class);  
  16. //③ 基於註解模擬類  
  17. @Mock  
  18. User mockUser;  
  19.      
  20.     @Before   
  21.     public void initMocks() {  
  22.     //④ 初始化當前測試類全部@Mock註解模擬對象  
  23.         MockitoAnnotations.initMocks(this);  
  24.     }  
  25.      …  
  26.  }  
  27. …  



   在①處和②處,經過Mockito提供的mock()方法建立UserService 用戶服務接口、用戶服務實現類UserServiceImpl的模擬對象。在③處,經過@Mock註解建立用戶User類模擬對象,並須要在測試類初始化 方法中,經過MockitoAnnotations.initMocks()方法初始化當前測試類中全部打上@Mock註解的模擬對象。若是沒有執行這一 步初始化動做,測試時會報模擬對象爲空對象異常。

設定Mock對象的指望行爲及返回值

   從上文中咱們已經知道能夠經過when(mock.someMethod()).thenReturn(value)來設定Mock對象的某個方法調用時 的返回值,但它也一樣有限制條件:對於static和final修飾的方法是沒法進行設定的。下面咱們經過實例來介紹一下如何調用方法及設定返回值。 框架

Java代碼   收藏代碼
  1. import org.junit.Test;  
  2. import org.mockito.Mock;  
  3. import com.baobaotao.domain.User;  
  4. import com.baobaotao.service.UserService;  
  5. import com.baobaotao.service.UserServiceImpl;  
  6. …  
  7. public class MockitoSampleTest {  
  8.      …  
  9.   
  10.      //① 模擬接口UserService測試  
  11.     @Test  
  12.     public void testMockInterface() {  
  13.           //①-1 對方法設定返回值      
  14.     when(mockUserService.findUserByUserName("tom")).thenReturn(  
  15.                 new User("tom""1234"));  
  16. //①-2 對方法設定返回值    
  17. doReturn(true).when(mockServiceImpl).hasMatchUser("tom""1234");  
  18.  //①-3 對void方法進行方法預期設定   
  19. User u = new User("John""1234");  
  20.     doNothing().when(mockUserService).registerUser(u);  
  21.   
  22. //①-4 執行方法調用  
  23.         User user = mockUserService.findUserByUserName("tom");  
  24.         boolean isMatch = mockUserService.hasMatchUser("tom","1234");  
  25.          mockUserService.registerUser(u);  
  26.   
  27.         assertNotNull(user);  
  28.         assertEquals(user.getUserName(), "tom");  
  29.         assertEquals(isMatch, true);  
  30.     }  
  31.   
  32.     //② 模擬實現類UserServiceImpl測試  
  33. @Test  
  34.     public void testMockClass() {  
  35.           // 對方法設定返回值  
  36.         when(mockServiceImpl.findUserByUserName("tom"))  
  37. .thenReturn(new User("tom""1234"));  
  38.     doReturn(true).when(mockServiceImpl).hasMatchUser("tom""1234");  
  39.   
  40.         User user = mockServiceImpl.findUserByUserName("tom");  
  41.         boolean isMatch = mockServiceImpl.hasMatchUser("tom","1234");  
  42.         assertNotNull(user);  
  43.         assertEquals(user.getUserName(), "tom");  
  44.         assertEquals(isMatch, true);  
  45.     }  
  46.       
  47.     //③ 模擬User類測試  
  48. @Test  
  49.     public void testMockUser() {  
  50.         when(mockUser.getUserId()).thenReturn(1);  
  51.         when(mockUser.getUserName()).thenReturn("tom");  
  52.         assertEquals(mockUser.getUserId(),1);  
  53.         assertEquals(mockUser.getUserName(), "tom");  
  54.     }  
  55. …  


 
   在①處,模擬測試接口UserService的findUserByUserName()方法、hasMatchUser()方法及 registerUser()方法。在①-1處經過when().thenReturn()語法,模擬方法調用及設置方法的返回值,實例經過模擬調用 UserService 用戶服務接口的查找用戶findUserByUserName()方法,查詢用戶名爲「tom」詳細的信息,並設置返回User對象:new User("tom", "1234")。在①-2處經過doReturn (). when ()語法,模擬判斷用戶hasMatchUser()方法的調用,判斷用戶名爲「tom」及密碼爲「1234」的用戶存在,並設置返回值爲:true。在 ①-3處對void方法進行方法預期設定,如實例中調用註冊用戶registerUser()方法。設定調用方法及返回值以後,就能夠執行接口方法調用驗 證。在②處和③處,模擬測試用戶服務實現類UserServiceImpl,測試的方法與模擬接口一致。

驗證交互行爲

   Mock對象一旦創建便會自動記錄本身的交互行爲,因此咱們能夠有選擇地對其交互行爲進行驗證。在Mockito中驗證mock對象交互行爲的方法是 verify(mock). xxx()。因而用此方法驗證了findUserByUserName()方法的調用,由於只調用了一次,因此在verify中咱們指定了times參數 或atLeastOnce()參數。最後驗證返回值是否和預期同樣。 dom

Java代碼   收藏代碼
  1. import org.junit.Test;  
  2. import org.mockito.Mock;  
  3. import com.baobaotao.domain.User;  
  4. import com.baobaotao.service.UserService;  
  5. import com.baobaotao.service.UserServiceImpl;  
  6. …  
  7. public class MockitoSampleTest {  
  8.      …  
  9.   
  10.      //① 模擬接口UserService測試  
  11.     @Test  
  12.     public void testMockInterface() {  
  13.           …  
  14.     when(mockUserService.findUserByUserName("tom"))  
  15.                          .thenReturn(new User("tom""1234"));  
  16. User user = mockServiceImpl.findUserByUserName("tom");  
  17.   
  18. //①-4 驗證返回值  
  19.         assertNotNull(user);  
  20.         assertEquals(user.getUserName(), "tom");  
  21.         assertEquals(isMatch, true);  
  22.   
  23.  //①-5 驗證交互行爲  
  24. verify(mockUserService).findUserByUserName("tom");  
  25.   
  26. //①-6 驗證方法至少調用一次  
  27. verify(mockUserService, atLeastOnce()).findUserByUserName("tom");  
  28. verify(mockUserService, atLeast(1)).findUserByUserName("tom");  
  29.       
  30. //①-7 驗證方法至多調用一次  
  31. verify(mockUserService, atMost(1)).findUserByUserName("tom");  
  32.     }  
  33. …  



   Mockio爲咱們提供了豐富調用方法次數的驗證機制,如被調用了特定次數verify(xxx, times(x))、至少x次verify(xxx, atLeast (x))、最多x次verify(xxx, atMost (x))、從未被調用verify(xxx, never())。在①-6處,驗證findUserByUserName()方法至少被調用一次。在①-7處,驗證 findUserByUserName()方法至多被調用一次。ide

 

http://stamen.iteye.com/blog/1470066工具

相關文章
相關標籤/搜索