日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core TDD 前传: 编写易于测试的代码 -- 构建对象

發(fā)布時(shí)間:2023/12/4 asp.net 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core TDD 前传: 编写易于测试的代码 -- 构建对象 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

該系列第1篇: 講述了如何創(chuàng)造"縫".? "縫"(seam)是需要知道的概念.

本文是第2篇, 介紹的是如何避免在構(gòu)建對象時(shí)寫出不易測試的代碼.?本文的概念性內(nèi)容大部分都來自Misko Hevery的這篇博客文章.

構(gòu)建

還是用上文里汽車的例子.

通常情況下, 我們是先去建造汽車, 組裝好汽車后, 我們再去駕駛它.

軟件開發(fā)也類似, 我們應(yīng)該把對象構(gòu)造完畢之后, 再去用它. 但是有時(shí)候, 開發(fā)者會(huì)在構(gòu)造過程中添加一些程序邏輯. 這就相當(dāng)于車還沒造完, 我們就駕駛它去兜風(fēng)了. 這樣做是不太好的.

構(gòu)造函數(shù)是類用來創(chuàng)建其實(shí)例對象的方法, 這里的代碼是用來準(zhǔn)備該對象的. 但有時(shí)開發(fā)者會(huì)在構(gòu)造函數(shù)里做一些其它的工作, 例如構(gòu)建依賴項(xiàng), 執(zhí)行初始化邏輯等等.

在構(gòu)造函數(shù)(或者更大一點(diǎn), 指構(gòu)建的過程)里, 做這些額外的工作會(huì)讓測試變得異常困難. 這是因?yàn)橄癯跏蓟蕾図?xiàng), 調(diào)用服務(wù), 設(shè)置狀態(tài)的邏輯等這些工作會(huì)把用于測試的"縫"弄丟. 導(dǎo)致無法進(jìn)行mock.

總之在構(gòu)造的過程中做太多的工作會(huì)妨礙測試.

?

危險(xiǎn)信號(hào)

  • 在構(gòu)造函數(shù)/字段聲明里出現(xiàn)new關(guān)鍵字

    • 如果構(gòu)造函數(shù)里需要?jiǎng)?chuàng)建依賴, 那么這就會(huì)為該類與依賴項(xiàng)之間創(chuàng)造了緊耦合. 這個(gè)之前提過, 所以需要注入依賴. 但是簡單的值類型, 例如字符串, List, Dictionary等還是可以的.

  • 在構(gòu)造函數(shù)/字段聲明里調(diào)用靜態(tài)方法

    • 靜態(tài)方法不可以被mock, 也不能被注入.

  • 構(gòu)造函數(shù)出現(xiàn)流程控制邏輯代碼

    • 這樣就很難對邏輯直接進(jìn)行測試了. 我們只能分別使用不同的方式構(gòu)造該對象, 測試并確認(rèn)對象的狀態(tài). 而這個(gè)狀態(tài)通常對直接測試是隱藏的. 實(shí)際上只要不是賦值代碼, 就有可能是問題代碼.

  • 構(gòu)造函數(shù)里出現(xiàn)非賦值代碼

  • 存在另外一個(gè)初始化函數(shù) (也就是說構(gòu)造函數(shù)走了完, 但是對象并沒有被完全初始化)

?

如何解決問題?

  • 不要在構(gòu)造函數(shù)里創(chuàng)建依賴項(xiàng), 應(yīng)該注入它們. 然后在構(gòu)造函數(shù)里把它們賦值給類的私有變量.

  • 當(dāng)需要構(gòu)建對象圖(一組有引用關(guān)系的對象), 也包括對象需要一些構(gòu)建的參數(shù)等情況, 應(yīng)該使用工廠, 建造者模式, 或者IoC容器的依賴注入等, 目的是把這些對象的構(gòu)建工作分離出去.

  • 避免在構(gòu)造函數(shù)里寫邏輯代碼, 例如條件, 循環(huán), 計(jì)算等等. 也不能把邏輯代碼放在別的方法, 然后調(diào)用該方法...

總之就是要避免對象的構(gòu)建和對象的行為混合到一起,?因?yàn)樗鼈冊谝黄鹁蜁?huì)很難進(jìn)行測試.

?

最后還有一點(diǎn), 首先你需要知道, 根據(jù)angular的創(chuàng)始人Misko Hevery所說:

對象的構(gòu)造分兩類, 一種是可注入的, 一種是可new的.

可注入的對象可以由其它的一堆可注入對象組成. 它們可以為 可new的 對象工作. 可注入的對象通常是實(shí)現(xiàn)了接口的service, 像什么IUnitOfWork, IRepository, IxxxService等等.

可new的對象就是對象圖里的終點(diǎn), 例如實(shí)體或者值對象(Value Object)等.

為了易于測試, 針對這兩類構(gòu)造, 有下列規(guī)則:

可注入的對象可以在構(gòu)造函數(shù)請求(注入)其它的可以注入對象, 但是不能在構(gòu)造函數(shù)請求可new的對象.

反過來, 可new的對象可以在構(gòu)造函數(shù)請求其它的可new對象, 但是不能在構(gòu)造函數(shù)請求可注入的對象.

?

例子

第一個(gè)例子

這是不對的, 構(gòu)建的過程中直接new的話, 就會(huì)造成緊耦合, 也無法在測試中使用Test Double來代替它們了. 如果測試中不代替它們的話, 有些服務(wù)的開銷可能會(huì)很大.

?

正確的寫法是使用依賴注入:

第二個(gè)例子

該例中, UserController只需要UserService和LoggingService兩個(gè)依賴項(xiàng). 但是UserService又依賴于UserRepository.?

但是這樣寫就不對了, 這會(huì)造成UserController和UserRepository間的緊耦合, 而且配置UserService也并不是UserController的責(zé)任.

?

正確的寫法是:

而UserService也最好是注入依賴.

?

而如果UserService并不是在構(gòu)造函數(shù)注入U(xiǎn)serRepository的話:

那么Controller里就應(yīng)該這樣寫:

不過最好還是使用構(gòu)造函數(shù)注入的寫法.

?

第三個(gè)例子

仔細(xì)的說, 該例有不止一處錯(cuò)誤.

首先它有條件判斷邏輯代碼; 此外它還使用了ApplicationState.IsRunning這個(gè)靜態(tài)變量(就是全局狀態(tài)); 而且在構(gòu)造函數(shù)里還做了UserService的配置工作, 這不是UserController的責(zé)任.

盡量要避免全局變量, 它無法進(jìn)行隔離, 測試會(huì)遇到麻煩, 例如并行測試時(shí)其中一個(gè)測試改變了靜態(tài)變量的值就可能導(dǎo)致另一個(gè)測試失敗.

但是粗略的說, 該例可以說就是一個(gè)錯(cuò)誤, 如何配置UserService并不是UserController的責(zé)任, 所以, 正確的做法是把UserService配置相關(guān)的代碼移出去, 讓它自己去管理吧:

?

第四個(gè)例子

該例子中, LoggingService的Log方法需要一個(gè)Area類型的對象, 它是一個(gè)值對象.

所以它的錯(cuò)誤就是, 不應(yīng)該把可new的對象注入到可注入的對象里. 這么做的話, 測試就不好做隔離了.

?

正確的做法應(yīng)該是, 作為方法的參數(shù)傳遞進(jìn)來:

第五個(gè)例子

如果出現(xiàn)類類似initalize()或類似意思的方法, 很有可能說明該對象的責(zé)任太多了.

?

修改它很簡單, 讓各自的類負(fù)責(zé)自己的內(nèi)容即可. 去掉initialize()方法即可.

?

例子就舉這些, 并不全, 詳細(xì)請看Angular作者的博文.

?

測試/運(yùn)行時(shí)如何建立對象

上面例子里的UserController就是我們需要使用的對象, 在運(yùn)行時(shí), 代碼可能是這樣的:

構(gòu)建這個(gè)對象還是有點(diǎn)麻煩的, 它的類關(guān)系圖如下:

?

所以測試的設(shè)置過程也會(huì)比較麻煩:

當(dāng)然也可以不直接new, 而是使用mock. 總之都很麻煩.

?

使用工廠

所以我們可以使用Factory等模式, 把構(gòu)建UserController的工作放到工廠里:

?

可以這樣調(diào)用:

?

使用IoC容器

如果項(xiàng)目使用了IoC容器的話, 還可以使用類似下面的用法:

?

先介紹到這里.

原文地址:http://www.cnblogs.com/cgzl/p/9375655.html

.NET社區(qū)新聞,深度好文,歡迎訪問公眾號(hào)文章匯總 http://www.csharpkit.com

總結(jié)

以上是生活随笔為你收集整理的.NET Core TDD 前传: 编写易于测试的代码 -- 构建对象的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。