陳麗萍 張 勇 丁智敏
(巢湖學院計算機與信息工程學院,安徽 巢湖 238000)
單元測試是極限編程的基礎[1,2],單元測試的目的是保證每個程序模塊能夠正確運行。然而在實施單元測試驅動開發過程中,往往和被測模塊交互的系統內某個模塊或系統外實體尚未開發出來,此時可借助Mock Objects技術[3]。它從測試中分離了外部不需要的因素并且幫助開發人員專注于被測試的功能[4]。EasyMock框架[5]是Mock Objects技術的實現框架。它通過使用動態代理提供對接口和類創建Mock對象,并利用Mock對象模擬協同模塊,從而使單元測試順利進行。在開發時,將EasyMock、Junit集成到IDE中可方便的完成自動化測試[6]。本文在總結EasyMock框架的特點基礎上,介紹了基本測試框架和分析了功能優缺點,并且針對缺點給出了相應的解決方案。

圖1 EasyMock功能類
EasyMock[7]擁有很多的API函數,可以直接用在測試程序中,其主要的功能類結構如圖1所示:
EasyMock框架中與用戶聯系最緊密的類EasyMock類,該類提供了create,replay和verify mock對象等靜態方法以及一系列的標準的參數匹配器。
IMocksControl主要用于創建Mock對象,并負責管理Mock對象。
IExpectionSetters接口主要是對Mock對象期望調用的方法進行一些相關的設置。
MocksControl類主要提供了用于添加Mock對象的預期行為和實際調用的方法。
通過EasyMock創建Mock對象,并應用Mock對象來模擬外部協同模塊,完成單元測試,需經歷五個步驟。如圖2所示:

圖2 EasyMock應用步驟
首先創建被模擬模塊的Mock對象,這里可以通過兩種方式來完成。一種是使用EasyMock的creatMock方法直接創建;另一種是通過Easy-Mock類的createControl方法,先創建IMockControl對象,進而創建Mock對象。
接下來錄制Mock對象的預期行為即為創建的Mock對象設定預期的方法調用和方法調用所產生的輸出。
接著在使用Mock對象進行實際的測試前,需要將Mock對象的狀態切換為replay狀態。設置Mock對象為replay狀態可以使用replay方法。
之后是執行需要測試的業務代碼并檢查代碼是否返回正確的結果。
最后是驗證。它來驗證整個過程中,Mock對象是否真正完成了record階段的設定。
Easymock通過檢查附加給測試響應的斷言來驗證測試的結果和交互行為是通過還是失敗,有大量的Junit的斷言可供選擇。軟件結構的設計會導致Easymock框架的使用情況。具體優缺點分析如下:
4.1.1 EasyMock的優點
(1)EasyMock提供了動態構建Mock對象的方法,而無需開發人員額外實現Mock對象的代碼,很大程度上減少了工作量。
(2)EasyMock可以完成多種Mock對象的測試場景。例如:當真實協同對象實際還不存在(如需要和其他開發人員開發的模塊交互);當真實協同對象行為難于觸發(如根據需要制造網絡故障)等。
4.1.2 EasyMock的缺點
(1)EasyMock通過創建Proxy的方式來實現的Mock對象。因而Mock對象不能訪問靜態方法,這就造成EasyMock不能模擬靜態方法。
(2)EasyMock通過創建Proxy的方式來實現的Mock對象,同時Java的封裝特性,使得Mock對象不能訪問私有方法和final方法。這就造成EasyMock不能模擬私有方法和final方法。
(3)EasyMock 不能對 Object類的 equals(),hasCode()、toString()方法模擬。 也就是說,Easy-Mock對這幾種方法模擬時,無法改變其行為,它們仍舊按照默認的方式執行。此問題還有待于進一步的研究和分析。
EasyMock不能模擬靜態方法。為了克服該缺點,這里可以通過整合EasyMock和PowerMock。PowerMock使用CGLib來操縱字節碼來實現靜態方法的Mock對象,因此可以對靜態方法模擬支持[8]。因此在Mock方法時,使用EasyMock模擬普通方法,用PowerMock去模擬靜態方法。Easy-Mock和PowerMock的結合可以更好地實現測試。
EasyMock不能模擬私有方法和final方法。為了解決該問題,可以通過使用字節碼技術修改class文件中字節碼的關鍵詞,將私有方法變為公有方法和final修飾符去掉。在這里借助Java的字節碼操作工具javassist,因為javassist可以在不需要了解虛擬機指令前提下,使用java編碼形式動態對類轉換、修改class中的方法。這樣可以將含有final或private修飾符的類在被當前JVM加載前對此類修改。這樣去除final和private標識符后,進而使用EasyMock進行測試。
5.1.1 示例程序 1
使用EasyMock框架對一個應用程序測試。這里的應用程序結構設計如圖3所示:

圖3 結構設計(1)
這里待測試類為Calculator,Calculator類中add方法完成兩個整數之和,而add方法使用了Math類的addInteger方法。Calculator類和Math類對應代碼如下:


5.1.2 示例程序 2
使用框架對另一個應用程序測試。這里的應用程序結構設計如圖4所示:

圖4 結構設計(2)
這里待測試類為Calculator,Calculator類中add方法完成兩個整數之和,而add方法使用了Math類的addInteger方法。Calculator類和Math類對應代碼如下:


這里測試平臺為 Eclipse-sdk-3.3, 使用JUint4.10 和 EasyMock3.2。將 EasyMock 框架與注入解決方案后的EasyMock框架分別對示例程序1,2進行測試。
使用EasyMock框架和注入解決方案后的EasyMock框架對示例程序1測試,測試結果分別如圖5的(a),(b)所示:

圖5 對示例程序1測試結果
使用EasyMock框架和注入解決方案后的EasyMock框架對示例程序2測試,測試結果分別如圖6 的(a),(b)所示:

圖6 對示例程序2測試結果
由此可見,EasyMock框架在模擬對象時有一些限制:不能對靜態方法、私有方法和final方法模擬,導致測試失敗。而使用了注入解決方案后的EasyMock框架可以應用于更多的測試情況,應用范圍更廣。
由于IT界對測試驅動開發技術的持續關注與偏愛,軟件單元測試也成為人們研究的熱點,EasyMock是單元測試的Mock工具之一。本文研究了EasyMock測試框架和應用;關注該工具在實際測試中的可行性,并且根據實際的需要采用整合Powermock框架和利用字節碼操作工具javassist對類文件的修改來減少EasyMock框架的Mock對象的限制,測試結果證明了更好地滿足實際的需要。
[1]Erickson J,Lyytinen K,Siau K.Agile Modeling,Agile Software Development,and Extreme Programming:The State of Research[J].Journal of Database Management,2005,(4):88-100.
[2]Mordinyi R,Kuhn E,Schatten A,Towards an Architectural Framework for Agile Software Development[C]//International Conference and Workshops on Engineering of Computer Based Systems.American,2010:276-280.
[3]Mackinnon T,Freeman S,Craig P.Endo-Testing:Unit Testing with Mock Objects[C]//Proceeding of the eXtreme Programming and Flexible .Italy,2000:21-23.
[4]Jafadeesh Nandigam,Tao Yonglei.Using mock object frameworks to teach object-oriented design principles[J].Journal of Computing Sciences in Colleges,2010,(1):40-48.
[5]Tammo,Freese.EasyMock:Dynamic Mock Objects for Junit[C]//Proceedings of the 3rd International Conference on Extreme Programming and Agile Processes in Software Engineering.Italy,2002:1-5
[6]Petar Tahchiev,Felipe Leme,Vincent Massol,et al.Junit實戰[M].王魁,譯.北京:人民郵電出版社,2012:96-129.
[7]EasyMock 官方網站[EB/OL].(2013-7-11)[2014-1-29]http://www.easymock.org/.
[8]PowerMock 官方網站.[EB/OL].(2013-11-19)[2014-1-29]http://www.Powermock.org/.