1. 程式人生 > >單元測試(二)-樁對象

單元測試(二)-樁對象

依賴項 基本原則 異常 spa log 控制 對象 工廠類 處理

在單元測試時,難免會碰到一些外部依賴,外部依賴是指在系統中代碼與其交互的對象,而且無法對其做人為控制,比如文件系統、線程、內存、時間、數據庫結果集等,這時可以使用偽對象(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

單元測試(二)-樁對象