EasyMock使用手記
Mock 對象能夠模擬領域對象的部分行為,并且能夠檢驗運行結果是否和預期的一致。領域類將通過與 Mock 對象的交互,來獲得一個獨立的測試環境(引自《 精通 Spring——Java 輕量級架構開發實踐 》。
在模仿對象中,我們定義了四個概念:
1 )目標對象:正在測試的對象
2 )合作者對象:由目標對象創建或獲取的對象
3 )模仿對象:遵循模仿對象模式的合作者的子類(或實現)
4 )特殊化對象:覆蓋創建方法以返回模仿對象而不是合作者的目標的子類
一般來說,應用模仿對象的過程如下:
1 )創建模仿對象的實例
2 )設置模仿對象中的狀態和期望值
3 )將模仿對象作為參數來調用域代碼
4 )驗證模仿對象中的一致性
那么,我們應該如何以及在哪里使用 Mock 對象呢?一般來說,對于目標對象中的合作者對象,在測試時如果其狀態或行為的實現嚴重地依賴外部資源(比如數據持久化中的 DAO ,比如負責發送電子郵件的類),或者團隊并行開發時,目標對象的合作者對象并沒有實現(比如 J2EE 中,橫向分工時,負責 Action 的調用 Service ,負責 Service 調用 DAO 時,相應的 Service 及 DAO 沒有實現),這時我們就需要模仿這些類。其實,在做 J2EE 時,傳統的 N 層架構中,我們都是面向接口編程的,我們定義了 DAO 接口,我們定義了 Service 接口,這樣做的優點就是我們在測試時可以構造實現接口的 Mock 類。這里不得不提依賴注入,通過依賴注入,我們才能在測試時 set Mock 對象。這也說明,為了方便測試,我們不得不一步一步 重構代碼,而模式就在重構中自然地產生了。
對于 Mock 對象,我們可以根據 合作者接口(或者是類) 實現具體的 Mock 類,這樣的 Mock 類實際上是 Stub 。有些情況下, Stub 是必要的。但對于諸如 DAO 、 Service ,我們只關心在給定參數的情況下,調用的方法能夠返回預期的值,我們根本不關心其內部實現,這時候如果使用 Stub 的話就會產生不必要的代碼。我們需要的就是能 動態地生成 Mock 對象而不需要編寫它們的工具, EasyMock 就是這樣的工具。
EasyMock 是一個 Mock 對象的類庫,現在的版本是 2.0 ,這個版本只支持 Mock 接口,如果需要 Mock 類,需要下載它的擴展包。
下面通過一個具體的例子說明一下 Mock 對象的使用,我寫的例子就是測試 Service 類中的一個方法, Mock 的對象是 DAO 。
首先是一個簡單的實體 bean Account:








































一個只含一個方法的 DAO AccountDAO:







一個同樣只含一個方法的 AccountService 接口:







與 AccountService 相應的實現類:























這里我沒有實現 AccountDAO 接口,對于 Mock 測試來說,這也是不需要的。下面就是 AccountServiceImpl 的測試類 AccountServiceTest :







































下面簡要的說明一下 Mock 對象的工作過程:
1 )在 setUp() 中,通過 “accountDAOMock = createMock(AccountDAO.class);” (這里使用了 java5 中的靜態導入),創建 AccountDAO 的 Mock 對象,由于 EasyMock 采用了范型技術,故創建的 Mock 對象不需要強制類型轉換。然后通過 “accountService.setAccountDAO(accountDAOMock);” 設置目標對象的合作者對象。
2 )對于測試方法 “testGetAccount()” , (a) 處的 reset() 方法是將 Mock 對象復位,也就是重新設置 Mock 對象的狀態和行為。由于此處是第一次調用 Mock 對象,可以不必使用 reset() 方法。
3 ) (b) 處 expect() 是錄制 Mock 對象方法的調用,其參數就是 Mock 對象的方法,其中如果調用的方法有返回值,要通過 andReturn() 方法設置預期的返回值。
4 ) (c) 處的 replay() 是結束錄制過程。 在調用 replay() 方法之前的狀態, EashMock 稱之為 “record 狀態 ” 。該狀態下, Mock 對象不具備行為(即模擬接口的實現),它僅僅記錄方法的調用。在調用 replay() 后,它才以 Mock 對象預期的行為進行工作,檢查預期的方法調用是否真的完成。
5 ) (d) 處的 verify() 是用于在錄制和回放兩個步驟完成之后進行預期和實際結果的檢查。這里就是檢查 accountDAOMock 是否如預期一樣調用了 getByNameAndPwd 方法。
對于上面的舉例,它可能并不具有實際的價值,這里我只想拋磚引玉。在 N 層架構的 Java 程序中, Mock 對象在單元測試中正發揮著越來越重要的作用。我現在看到的是,在 Service 層與 Web 層, Mock 對象能很好的被應用。有人覺得在 Persistence 層也應該使用 Mock 對象,但就像我們所知道的,在使用 Hibernate 、 Ibatis 等 ORM 工具的情況下,我們的 Persistence 層的測試主要測試的就是那些配置文件、查詢語句等(實際上是集成測試),如果還 Mock 的話,就失去了測試的意義。
對于 Mock 的更多的信息,你可以訪問Mock Objects,在本文寫作的過程中,參考了使用模仿對象進行單元測試
等文章,一并感謝。
posted on 2007-11-24 07:22 々上善若水々 閱讀(1728) 評論(0) 編輯 收藏 所屬分類: 軟件測試