前面使用了樁對象來解除被測代碼對外部的依賴,以便于獨立地測試代碼的内部邏輯。但樁對象隻能測試系統的傳回值或者狀态的改變,如果要測試對象之間的互動,則需要使用模拟對象。
1 三種測試類型
a) 三種類型的測試分别為:
Value-based testing測試方法的傳回值
State-based testing 測試狀态的改變
Interaction testing 測試對象之間的互動
b) 互動測試測試的是某種特定的行為(如給另一個對象傳遞資訊)。作者認為,互動測試應該盡量少用,在能使用值測試和狀态測試的場合,就不要使用互動測試,因為互動測試會增加複雜度
c) 用自動給植物澆水的裝置舉例來說明狀态測試與互動測試的差別:狀态測試就是在一天結束後,通過檢視土壤中的水分、植物的生長情況來測試澆水效果;而互動測試則要實時記錄澆水次數、水量等資訊。狀态測試得出結論需要一定的過程和準備工作;但互動測試實施起來卻不友善
d) 樁對象用于值測試和狀态測試,模拟對象則用于互動測試
2 樁對象與模拟對象的差別
模拟對象是一種可以記錄對自身調用情況的僞對象。模拟對象可用于互動測試,可認為是在樁對象的基礎上,增加了記錄調用情況的功能。它們之間的差別如下:
a) 樁對象(stub)替代被測代碼對第三方對象的依賴,然後要針對被測代碼進行斷言
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iNyQzM0IDO1cTMtcTNzczMyEDMyUjM1AzNxAjMtUDMyQDN48CX1AzNxAjMvwVNwIDN0gzLcd2bsJ2Lc12bj5ycn9Gbi52YuUTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
b) 模拟對象(mock)則模拟第三方依賴,然後為了判斷被測代碼是否與第三方依賴正确互動,要針對模拟對象進行斷言
c) 模拟對象可以讓測試失敗,但樁對象不會
3 模拟對象的應用
a) 還是使用之前的LogAnalyzer類,假設業務場景為:當檔案名稱太短時,調用web服務發送資訊。但web服務是外部依賴,web傳輸也比較費時,而且現在測試的是LogAnalyzer對web服務的調用行為(互動)。
LogAnalyzer如下
在測試代碼中,将FakeWebService對象作為模拟對象傳遞給LogAnalyzer,并要針對FakeWebService對象進行斷言。
在模拟對象中,屬性LastError會記錄上一次互動的内容以供斷言
b) stub和mock有時需要同時使用,比如測試場景改為:調用WebService時可能會發生異常,如果有異常,則發送郵件給管理者,測試要做的是觀察發送郵件行為是否正确。
LogAnalyzer比上一個的複雜
在測試代碼中,FakeWebService作為樁對象,FakeEmailService作為模拟對象,首先設定FakeWebService要抛異常,最後針對FakeEmailService記錄的郵件内容進行斷言,測試LogAnalyzer與其的互動是否正确
可以設定FakeWebService是否要抛異常
FakeEmailService作為模拟對象,與上一個例子中FakeWebService的職責類似,記錄互動資訊以供斷言
以上便是stub與mock的差別,以及mock的基本使用方法,在第二個例子中,同時使用了stub與mock,最後針對mock進行斷言,這也是mock使用的基本原則,一個測試中最多隻能有一個mock,其它都為stub;mock存在時便隻對mock斷言;在一個測試中隻應該驗證一件事,如果有多個mock意味着在測試不止一件事。
參考資料:
The Art of Unit Testing with examples in
C#, 2nd Edition by Roy Osherove