1. 程式人生 > >單元測試中mock的使用及mock神器jmockit實踐

單元測試中mock的使用及mock神器jmockit實踐

在最近的r應用的單元測試中,經常需要用到mock,可以說mock在ut (unit test)中是無處不在的。而在r的ut實踐中也找到了一種很簡潔的mock方式,不僅解決了ut中所有需要mock的地方,而且可以很少量的程式碼來完成mock。詳見下文。

  一.Mock的使用場景:

  比如以下場景:

  1. mock掉外部依賴的應用的HSF service的呼叫,比如uic,tp 的hsf服務依賴。

  2. 對DAO層(訪問mysql、oracle、tair、tfs等底層儲存)的呼叫mock等。

  3. 對系統間非同步互動notify訊息的mock。

  4. 對method_A裡面呼叫到的method_B 的mock 。

  5. 對一些應用裡面自己的 class(abstract, final, static),interface,annotation ,enum,native等的mock。

  二. Mock工具的原理:

  mock工具工作的原理大都如下:

  1. record階段:錄製期望。也可以理解為資料準備階段。建立依賴的class 或interface或method ,模擬返回的資料,及呼叫的次數等。

  2. replay階段:通過呼叫被測程式碼,執行測試。期間會invoke 到 第一階段record的mock物件或方法。

  3. verify階段:驗證。可以驗證呼叫返回是否正確。及mock的方法呼叫次數,順序等。

  三. 當前的一些Mock工具的比較:

  歷史曾經或當前比較流行的Mock工具有EasyMock、jMock、Mockito、Unitils Mock、PowerMock、jmockit等工具。

  他們的功能對比如下:

  從這裡可以看到,當前為什麼jmockit為什麼這麼火爆了!所以我們的UT中的mock工具也選擇了目前無所不能的jmockit。

  而在使用的過程中,感覺到jmockit的 Auto-injection of mocks 及 Special fields for "any" argument matching  及各種有用的 Annotation 給測試程式碼精簡和測試效率提升帶來了實實在在的好處。



  可以看到jmockit常用的Expectation、NonStrictExpectations 期望錄製 及Annotation @Tested、@Mocked,@NonStrict、@Injectable 等簡潔的mock程式碼風格。而且jmockit 還自帶了code coverage的工具供本地UT時候看邏輯覆蓋或程式碼覆蓋率使用。

  五.Jmockit的實踐:

  第一步:新增jmockit的jar包依賴

  在refund的單元測試過程中,第一步:應用pom中引入jmockit的jar包,可以順帶引入jmockit自帶的code coverage的jar。

  第二步:一個完整的Jmockit的示例:


  這個是對refundmanager裡面查詢可退款金額範圍 queryRefundFeeRange的單元測試。通過看被測程式碼可以看到這個方法的實現裡面呼叫了

  feeResultDO = confirmGoodsService.queryConfirmToSellerRefundFee(detailId);

  這個外部的hsf依賴 獲取了feeResultDO 。

  這個hsf呼叫是需要mock的。

  傳統的mock或ut ,對 confirmGoodsService 這個bean是需要初始化,通過spring的配置,初始化載入等 一大堆程式碼。

  jmockit通過了註解的方式:

@Injectable
private ConfirmGoodsService confirmGoodsService ;

  一個Annotation就搞定了所有的配置,載入等問題。直接複用開發程式碼裡面的bean,節省了大量的程式碼。

  另外  @Tested  RefundManagerImpl refundManagerImpl = new RefundManagerImpl();

  這裡也用到了註解 @Tested   表示被測試的class 。

  另外還有常用的註解:@Mocked,@NonStrict等。

  而這段程式碼就是mock的核心:錄製被mock的method的行為及期望返回:

new Expectations(){
{
confirmGoodsService.queryConfirmToSellerRefundFee(anyLong);
result = feeResultDOmock;
times = 1;
}
} ;

  其中

  result  可以返回任意需要的測試型別;

  times  表示期望被呼叫的次數。

  是不是看起來非常簡潔明瞭。

  而上面該段程式碼如果 換成基於狀態的mockup 程式碼如下:

  採用MockUp的方式,可以mock任意的mock物件或方法,因為它直接改寫了原method的實現邏輯,直接返回需要的資料。

  這也是jmockit彪悍的地方之一。

  最後資料回收,防止各個testcase的mock相互影響的方式:

  Mockit.tearDownMocks();

  這一步也可以省略。

  還要重點介紹的就是mock期望裡面的入參 any。


這個any系列的萬能入參型別,也可以節省很多mock程式碼,可以高效的準備任何入參型別。


  以上,一個最簡單的,也最實用的jmockit的示例。


  jmockit的更多,對interface及method的單元測試的示例,將在後續總結匯總。


  六、 Jmockit自帶的code coverage :


  工程的 pom檔案中引入 jmockit-coverage 後,本地eclipse啟動單元測試後, 會自動統計單元測試的程式碼覆蓋率。關於行覆蓋,方法覆蓋,類覆蓋,分支邏輯覆蓋等各種資料都可以看到。


  IDE啟動UT時候,載入 code coverage  元件,


  點選進去,可以看到具體的覆蓋邏輯:


  其中綠色部分表示原始碼被run過。


  程式碼覆蓋對指導單元測試的測試邏輯,覆蓋等提供了直觀的指示。


  以上,就是在單元測試中mock技術的應用:Jmockit的使用介紹及實際應用示例。它在單元測試中確實可以很少的程式碼mock掉外部依賴,提高ut的效率,並且 自帶的code coverage可很方便的看到ut對被測程式碼的覆蓋效果,指導測試設計。