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








































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







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







與 AccountService 相應(yīng)的實(shí)現(xiàn)類:























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







































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