單元測試(二)-樁對象
在單元測試時,難免會碰到一些外部依賴,外部依賴是指在系統中代碼與其交互的對象,而且無法對其做人為控制,比如文件系統、線程、內存、時間、數據庫結果集等,這時可以使用偽對象(fake)來替代外部依賴,樁對象(stub)便是其中之一
一 樁對象
a) 樁對象是對系統中現有依賴項的一個替代品,可人為控制。通過使用樁對象,無需涉及依賴項,即可直接對代碼進行測試。使用樁對象可以輕松地控制模擬依賴項的返回值會行為(比如模擬內存溢出異常)。
b) 使用樁對象的前提是要找到原系統中的接縫(Seam)。接縫是指代碼中可以插入不同功能(如樁對象類)的地方。有時需要通過重構來制造接縫並解除依賴的方法,可以抽取接口來制造接縫,這樣底層實現就可以被替換了。
二 替換底層實現的方法
常用的替換底層實現的方法有:
在構造函數中接收一個接口實現(構造函數註入)
在屬性中接收一個接口實現(屬性註入)
在方法的參數中接收一個接口實現(參數註入)
使用工廠方法產生接口實現
a) 在構造函數中接收一個接口實現
如果要使用類LogAnalyzer中的IsValidLogFileName方法來判斷文件名是否有效,在生產環境下,IsValidLogFileName方法要讀取配置文件,然後根據配置內容來進行判斷,這個方法依賴文件系統。重構過程如下:
抽取IExtensionManager
通過構造函數傳遞IExtensionManager的實現,判斷文件名是否有效交給IExtensionManager.IsValid來做
在測試代碼中,構造偽對象FakeExtensionManager並傳遞給LogAnalyzer
偽對象FakeExtensionManager是可控的
b) 屬性註入和參數註入與構造函數註入的實現方法是類似的,但通過構造函數或參數註入意味著參數是必須的,而屬性註入卻是可選的,所以使用屬性註入時要註意默認註入的處理。
c) 使用工廠產生接口實現
在工廠類中,Create()方法默認返回生產環境的FileExtensionManager,測試代碼可以通過SetManager傳遞偽對象
LogAnalyzer通過Create方法取得IExtensionManager的接口實現
在測試代碼中,調用ExtensionManagerFactory.SetManager(**)註入FakeExtensionManager偽對象
d) 除了上述方法,還有很多方法可以達到解除依賴的目的,比如基類、重寫等方式。但不管哪種方法,為了測試而重構可能會破壞一些面向對象的基本原則,比如封裝。這就需要努力在可測試性和封裝之間取得平衡,雖然這比較困難,但保證可測試性也是很重要的。
參考資料:
The Art of Unit Testing with examples in C#, 2nd Edition by Roy Osherove
單元測試(二)-樁對象