Posted on 2008-07-07 22:14
切爾斯基 閱讀(2095)
評論(1) 編輯 收藏
預處理的接入點
-
構建腳本中的宏定義可以控制將文本解釋為真正的實現還是假的實現
-
構建腳本中的頭文件搜索路徑可被用來控制接入真正的聲明還是假的聲明
-
頭文件中的防衛宏可被用來接入假的聲明以遮擋真正的聲明被包含進來
上面第二點通常要求提供同名的頭文件, 而使用不同的構建設置
而第三點不要求同名, 只要使用相同的防衛宏,
并保證把包含替代聲明的頭文件放在真正的頭文件前面包含進來就可以, 使用同一構建設置即可
編譯鏈接的接入點
-
構建腳本中的庫搜索路徑可被用來控制接入真正的實現還是假的實現
-
針對接口編程, 可以控制接入真正的實現還是假的實現
-
與針對接口編程類似, 使用函數指針/函數對象等封裝函數, 可以接入假的實現
-
使用函數封裝數據訪問, static訪問, new 操作符, 可以子類化以接入假的實現
-
改造為模板, 可以傳入不同的模板實參來控制接入真正的實現還是假的實現
-
利用名稱作用范圍, 同名局部變量可遮擋其它名字等, 來控制接入真正的實現還是假的實現
-
利用對象的內存布局, 直接將某幾個假的函數地址組成的 vtable, 強轉為被測類型
運行時的接入點
-
解釋型動態語言, 直接在測試前重定義
-
編譯型靜態語言, 運行時修改函數地址
基本上兩個原則
-
尋找, 引入, 利用一切可能的接入點
-
在一切依賴具體實現的地方, 插入一層間接
例子: 測試使用了 static 函數, new 操作符的客戶代碼
在 TDD 流行之后, 關于 Singleton, static 函數, new
操作符等依賴具體實現的代碼被推薦避免使用. 遺留系統中則不可避免的保留著. 它們被批評的原因是使它們的客戶代碼難以測試. 我們可以利用
"使用函數封裝數據訪問, static訪問, new 操作符, 可以子類化以接入假的實現" 來進行測試.
public
class
SingletonClient {
publicvoid
doSomething(){
SingletonObject service = SingletonObject.instance();
service.execute();
}
}
上面的這個SingletonClient的doSomething是無法測試的, 因為引用了一個靜態函數.
可以把對靜態函數的引用抽取到一個可覆寫的函數中來引入接入點:
class SingletonClient {
publicvoid
doSomething(){
SingletonObject service = getServiceObject();
service.execute();
}
protected
SingletonObject getServiceObject() {
return SingletonObject.instance();
}
}
這樣測試時我們便可以子類化SingletonClient, 只重寫getServiceObject,
便可以針對這個子類進行測試, 效果與針對基類測試是一樣的(因為其它的函數實現都相同, 只是依賴的第三方對象被替換為了假的實現):
class SingletonClientInTestEnvironment
extends
SingletonClient {
protected
SingletonObject getServiceObject() {
returnnew
SingletonObjectStub();
}
}
例子: C++ 測試, 用遮蓋技術(定義同名服務類), 當同時需要測真正的服務類本身時如何解決鏈接問題? (重定義)
那就不要定義同名的類, #define 一個宏加一層間接, 恰當的時候 #undef. 如果是函數, 可以定義一個函數對象來封裝想假冒的函數,
生成該函數對象類型的一個變量時, 使用與函數相同的名字, 利用名字的作用域規則來遮蓋原來的函數.