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