使用stub破除external dependency

在測試中,如果因?yàn)榇a對外部資源存在依賴的行為,盡管代碼的邏輯是正確的也會(huì)存在測試失敗的可能性,這也被稱為test-inhibiting

常見的external dependency是系統(tǒng)中的一個(gè)對象,被測試的代碼需要與這個(gè)對象進(jìn)行交互,但是你并不能去控制這個(gè)對象的行為。

stub則是對于系統(tǒng)的external dependency的可控制的替代物。stub帶來的效果就是可以在測試代碼中無需對external dependency進(jìn)行直接處理。

提高代碼的可測試性

破除dependency最直接的方式就是引入seam,當(dāng)然這比如是需要refactoring配合的

A型方法:把具體類抽象成接口

抽取接口以便對實(shí)現(xiàn)進(jìn)行替換

B型方法:注入委托和接口(fake implementation)

在被測試類注入stub

在構(gòu)造函數(shù)注入偽對象

利用屬性注入的方式注入偽對象

在方法調(diào)用前注入問對象

抽取接口方式

public class ExternalManager:IExternalManager

{

public bool IsExternal (string name)

{

......

}

}

public interface IExternalManager

{

bool IsExternal(string name);

}

//測試單元

public bool IsExternalManager(string name)

{

IexternalManager mgr = new ExternalManager();

return mgr.IsexternalManager(name);

}

返回值為true的stub

public class AlwaysTrueExternalManager:IExternalManager

{

public bool IsExternal (string name)

{

return true;

}

}

在構(gòu)造函數(shù)中注入偽對象

這種方式需要給測試類添加新的構(gòu)造函數(shù)或者是個(gè)其構(gòu)造函數(shù)添加新的參數(shù),傳入抽取出來的接口類型的對象,通過field或var 供被測試方法或其它相關(guān)區(qū)域的調(diào)用。

通過這種方式,測試需要先進(jìn)行stub的配置,然后傳入被測試對象。這種方式可以提高測試代碼的可讀性,將需要被了解的信息集中在一點(diǎn)地方。

同時(shí),利用構(gòu)造函數(shù)添加參數(shù)的方式實(shí)現(xiàn)注入,實(shí)際上也使得這些參數(shù)稱為不可選的依賴項(xiàng),也就是說類的使用者需要為每個(gè)所需的特定依賴傳入?yún)?shù)。

利用構(gòu)造函數(shù)注入偽對象也可能會(huì)帶來一些問題,比如擁有多個(gè)external dependency 。這時(shí)候難道要添加多個(gè)構(gòu)造函數(shù)或是添加多個(gè)參數(shù)?(明顯是不明智的選擇)

這個(gè)問題可以通過創(chuàng)建特殊的類,擁有可以初始化一個(gè)類所需要的所有值,這樣構(gòu)造函數(shù)就只需要擁有這個(gè)類作為唯一參數(shù),當(dāng)然更好的方式是通過IoC去實(shí)現(xiàn)。

屬性注入

這個(gè)case需要為每個(gè)要注入的dependency添加一個(gè)屬性,包括get和set。然后只需要在被測函數(shù)中相應(yīng)的地方使用這些依賴。 與構(gòu)造函數(shù)的依賴注入方式一樣,需要指明必需的和可選的依賴項(xiàng),因此也會(huì)影響到api的設(shè)計(jì)。通過使用屬性,也同時(shí)說明了使用這個(gè)函數(shù),那些依賴項(xiàng)是不需要的(可能這也是需要使用的屬性注入的場景之一)。

方法調(diào)用前注入偽對象

這個(gè)case很明顯的的意思就是我在對這個(gè)對象進(jìn)行操作之前才可以得到其實(shí)例,而不是在構(gòu)造函數(shù)或?qū)傩灾芯鸵呀?jīng)準(zhǔn)備好,這個(gè)case的不同之處就是發(fā)起stub請求的對象就是被測試代碼。

這樣的話,可以通過使用工廠類來作為實(shí)例的提供者,當(dāng)然必須在構(gòu)造函數(shù)中初始化這個(gè)實(shí)例提供者。在這個(gè)工廠中,可以使用靜態(tài)方法返回實(shí)例了接口的對象實(shí)例,同時(shí)采用別的方式(如擴(kuò)展名)返回stub(這種方法會(huì)破壞類的設(shè)計(jì)封裝)。

返回stub的層次

第一層:在被測試類中偽造一個(gè)成員

添加構(gòu)造函數(shù),在構(gòu)造函數(shù)中設(shè)置類,從測試代碼中設(shè)置構(gòu)造參數(shù),還要考慮對api的影響。

這種方式會(huì)改變被測試類的語義,在沒有充分理由情況下一般不采用這種方式。

第二層:在工廠類偽造一個(gè)成員

上述方法調(diào)用前注入偽對象中提到的就是這一層,把工廠類的屬性設(shè)置成偽依賴項(xiàng),這種方式并不會(huì)改變語義,一切都是原樣,代碼也較為簡潔。但是使用這種方式,必須要充分了解實(shí)例的使用時(shí)間。

第三層:偽造工廠類

這種方式需要實(shí)現(xiàn)自己專有的偽工廠類,同時(shí)這個(gè)類也能有或者可能沒有接口,若沒有,則需要為這個(gè)工廠類創(chuàng)建接口,然后再創(chuàng)建偽工廠實(shí)例,讓其返回依賴項(xiàng)。這種層次可以理解為利用一個(gè)偽對象去返回一個(gè)偽對象,這個(gè)實(shí)在是很難想象的事情。

偽造方法

這種方法的層次,不同于上述幾種,相比而言,其更接近于被測試代碼(一般而言,約接近代碼,越少需要更改依賴項(xiàng))。很重要的一點(diǎn)就是這種方式將被測試的代碼也作為一個(gè)依賴項(xiàng)。

使用方式:

在測試類中:

添加返回真是實(shí)例的虛工廠方法

在代碼中正常的使用工廠方法

在測試項(xiàng)目中:

創(chuàng)建新的類

聲明這個(gè)新類繼承被測試類

創(chuàng)建需要替換的接口類型的公共字段(field)

重寫虛工廠方法

返回公共字段

測試代碼中

創(chuàng)建一個(gè)stub類的實(shí)例(實(shí)現(xiàn)相應(yīng)接口)

創(chuàng)建新的派生類而非被測試類的實(shí)例

配置新實(shí)例的公共字段(設(shè)置成stub實(shí)例)

在測試的派生類中,通過重寫工廠方法,產(chǎn)品代碼將使用配置的偽對象

這種抽取和重寫的方式,可以直接替換依賴項(xiàng),實(shí)現(xiàn)方法避免了大量和接口和虛函數(shù)。

這種方式比較適合用于模擬提供給被測試代碼的輸入,但是不適合驗(yàn)證被測試代碼到依賴項(xiàng)的調(diào)用。 如果被測試代碼是web服務(wù)時(shí),得到一個(gè)返回值,這是如果想要模擬自己的返回值的話,這種方法就很適合。但是如果想要測試代碼對于web服務(wù)的調(diào)用時(shí)是否正確就顯得捉襟見肘。 當(dāng)代碼中以及存在可以偽造的接口的時(shí)候,有明顯的可以使用seam的位置,便不需要使用這種方式。當(dāng)然如果面對一個(gè)密封類,這種方法也顯得很無力,無從下手。。。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,562評論 19 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,697評論 18 399
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,493評論 2 7
  • 本章內(nèi)容 理解對象屬性 理解并創(chuàng)建對象 理解繼承 面向?qū)ο笳Z言有一個(gè)標(biāo)志,那就是它們都有類的概念,而通過類可以創(chuàng)建...
    悶油瓶小張閱讀 964評論 0 1
  • 文章作者:Tyan博客:noahsnail.com 3.4 依賴 標(biāo)準(zhǔn)企業(yè)應(yīng)用不會(huì)由一個(gè)對象(或Spring用語中...
    SnailTyan閱讀 1,266評論 0 1

友情鏈接更多精彩內(nèi)容