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,在本文寫作的過程中,參考了使用模仿對象進行單元測試
等文章,一并感謝。