.NET Core TDD 前傳: 編寫易于測試的代碼 -- 全局狀態

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

第2篇,?避免在構建對象時寫出不易測試的代碼.

第3篇, 依賴項和迪米特法則.

本文是第4篇, 將介紹全局狀態引起的問題.

?

全局狀態

全局狀態, 也可以叫做應用程序狀態, 它是一組變量, 這些變量維護著應用程序的高級狀態.

在程序里, 全局狀態可能都存放在一個全局狀態對象里, 例如ASP.NET里面的HttpContext; 或者它們可能是全局的變量, 這些全局變量在程序的任何地方都可以訪問.

不管是如何實現的全局狀態, 每個全局狀態變量在內存里只有一個實例. 所以如果一個類里更新了全局變量的值, 那么另一個類訪問該變量的時候它的值就是剛才被更新的值.

有些情況下, 使用全局狀態確實有用; 但是如果使用不當, 則會對測試造成很大的影響.

?

全局狀態對測試引起的問題

  • 使用靜態方法或全局變量訪問全局狀態的時候, 就引起了對全局狀態的直接耦合. 這很不好.
  • 這種耦合就導致很難對測試進行設置. 針對每個測試, 我們必須創建和設置好存儲全局狀態的對象. 或者把全局變量設定為所需的值.
  • 因為每個全局狀態變量在內存里只有一個實例, 那么我們就無法進行并行單元測試了. 如果我們為A測試設定了全局變量的值, 然后在測試A結束前開始測試B, 這時測試B修改了全局變量的值, 這時測試A就可能會失敗, 因為它所期待的全局變量不是這個值.
  • 上面的這種現象就叫做鬼魅般的超距作用(Spooky Action at a Distance). 而實際項目中確實經常發生這樣的情況, 并行跑單元測試的時候偶爾會失敗, 而單獨去跑失敗的測試時卻一直成功. 這種耦合到全局狀態的測試就不能再稱為隔離測試了.

?

危險信號

  • 全局變量
  • 調用靜態字段或調用擁有靜態字段的類的靜態方法. 但也僅限于該類的靜態方法使用了該類的靜態字段.?
  • 單例模式 (Singleton Pattern)
  • 單元測試會隨機的失敗, 但是又沒發現明確的原因.

?

解決辦法

  • 盡量使用本地(局部, 越窄越好)狀態變量
  • 如果第三方庫使用了靜態方法, 那么應該使用一個包裝類來對該方法進行包裝. 這個包裝類還是要實現一個接口. 用它的時候注入該接口即可. 這樣測試的時候就可以為包裝類創建測試替身了, 并把全局狀態解耦.
  • 使用可依賴注入(IoC/DI)的單例體, 這種單例體是由IoC容器創建的.

?

例子

就舉一個例子吧.

有這樣一個獲取當前登錄用戶權限的類, 它使用的是單例模式:

這個是典型的單例模式, 它會保證在程序中只返回一個實例, 這里就不多介紹了.

?

下面這個Service會調用上面這個Auth類:

Auth是單例模式的, 而且還調用了靜態方法.

現在的狀態是, OfficeService和Auth所包含的全局狀態緊密的耦合到了一起.?

?

如何解決問題

首先應該把單例模式去掉, Auth類只保留兩個屬性和一個方法:

?

然后在service里面應該注入IAuth接口并使用:

?

那么接下來就需要保證這個IAuth無論在程序中注入了多少次, 都是同一個實例.

這時就需要使用依賴注入(DI) 庫了. 現在的DI庫通常允許指定IoC容器中每對綁定服務的作用范圍(Scope), 或叫做生命周期管理.

例如ASP.NET Core內置的IoC容器就內置了這種功能. 在ASP.NET Core 項目的Startup類里, 這樣寫就可以保證每次請求IAuth的時候只會得到同一個對象實例:

現在這個"單例"的工作是由IoC容器來負責了. 在其它地方正常的注入IAuth使用即可.

?

先寫到這, 本文的概念性內容和更多的例子請參考Angular創始的人這篇文章:?http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253922.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253922.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253922.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

(三)系統與架構級低功耗設計

前面講解了使用EDA工具(主要是power compiler)進行功耗分析的流程,這里我們將介紹在數字IC中進行低功耗設計的方法,同時也結合EDA工具(主要是Design Compiler)如何實現。我們的講解的低功耗設計主要是自頂向…

python---統計列表中數字出現的次數

1 import collections 2 3 a [1,2,3,1,2,3,4,1,2,5,4,6,7,7,8,9,6,2,23,4,2,1,5,6,7,8,2] 4 b collections.Counter(a) 5 for c in b: print c,b[c] 轉載于:https://www.cnblogs.com/lxs1314/p/7236321.html

MFC入門(一)——MFC是一個編程框架

MFC (Microsoft Foundation Class Library)中的各種類結合起來構成了一個應用程序框架,它的目的就是讓程序員在此基礎上來建立Windows下的應用程序,這是一種相對SDK來說更為簡單的方法。因為總體上,MFC框架定義了應用程序的輪廓,并…

2.數據結構筆記學習--線性表基本操作

線性表的結構定義: 順序表的結構定義: typedef struct {int data[maxSize]; //存放順序表元素的數組,一般用 int A[maxSize];int length; //存放順序表的長度,一般用 int n; }SeqList; 單鏈表結點定義: typedef struct L…

(四)RTL級低功耗設計

前面介紹了系統級的低功耗設計,換句話說就是在系統級降低功耗可以考慮的方面。系統級的低功耗設計,主要是由系統級設計、具有豐富經驗的人員實現,雖然還輪不到我們設計,我們了解一下還是比較好的。我們前端設計人員的重點不在系統…

Unity3D 游戲前端開發技能樹(思維導圖)

如果做游戲也是一種游戲,那么這個游戲的自由度實在是太高了.(導圖源文件鏈接:http://pan.baidu.com/s/1eSHpH5o 密碼:qzl5) 最近要用思維導圖軟件Xmind把自己的思路好好捋一捋,算是溫故知新吧. 轉載于:https://www.cnblogs.com/qiaogaojian/p/6098962.ht…

js forEach

forEach()函數從頭到尾把數組遍歷一遍。有三個參數各自是:數組元素。元素的索引,數組本身(假設是一個參數就是數組元素,也就是數組的值。var data[1,2,3,4,5,6]; var sum0; data.forEach(function(v){//當…

SQL Server 死鎖的告警監控

原文:SQL Server 死鎖的告警監控今天這篇文章總結一下如何監控SQL Server的死鎖,其實以前寫過MS SQL 監控錯誤日志的告警信息,這篇文章著重介紹如何監控數據庫的死鎖,當然這篇文章不分析死鎖產生的原因、以及如何解決死鎖。死鎖(D…

關于web性能一些特性匯總

關于web性能一些特性匯總 DOMContentLoaded & load load事件是window對象上的事件。指的是網頁資源已經加載完畢(包括但不限于DOM、圖片、音頻、腳本、插件資源以及CSS)。 DOMContentLoaded事件是document對象上的事件。指的是DOM已經加載完畢。IE中…

(五)門級電路低功耗設計優化

(1)門級電路的功耗優化綜述 門級電路的功耗優化(Gate Level Power Optimization,簡稱GLPO)是從已經映射的門級網表開始,對設計進行功耗的優化以滿足功耗的約束,同時設計保持其性能,即滿足設計規則和時序的要…

SQL三大范式

第一范式(1NF) (必須有主鍵,列不可分) 數據庫表中的任何字段都是單一屬性的,不可再分 create table aa(id int,NameAge varchar(100)) insert aa values(1,無限-女 ) 沒有達到第一范式 create table aa(id int,name varcahr(10),age char(2)) insert aa …

Spring3向Spring4升級過程中quartz修改

為什么80%的碼農都做不了架構師?>>> 問題 nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.scheduling.quartz.CronTriggerBean] for bean with name ... 原因 org.spri…

Socket編程知識必學/SELECT 編程

Select在Socket編程中還是比較重要的,可是對于初學Socket的人來說都不太愛用Select寫程序,他們只是習慣寫諸如 connect、accept、recv或recvfrom這樣的阻塞程序(所謂阻塞方式block,顧名思義,就是進程或是線程執行到這些…

EasyUI--messager

1.    alert 方法 <script type"text/javascript">$( function(){$.messager.alert("調用messager","文本內容") ;});</script> 這里還可以通過icon添加相應的圖標及info加入回調函數 <script type"text/javascript&quo…

ROS與navigation教程——基本導航調整指南

說明&#xff1a; 介紹如何調整機器人上的ROS導航包 步驟&#xff1a; (1) 機器人導航需要那些準備? 在調整新機器人上的導航包時遇到的大部分問題都在本地規劃器調諧參數之外的區域。機器人的里程計&#xff0c;定位&#xff0c;傳感器以及有效運行導航的其他先決條件常常…

小程序跨行跨列多列復雜表格實現

今天來實現個跨行跨列多列表格。 如圖&#xff0c;這是個列數不確定&#xff0c;有的單元格還要跨行跨列的復雜表格。 這里暫時最多支持4列&#xff0c;列數再多就放不下了。 實現原理 實現原理比較簡單&#xff0c;通過多個嵌套的循環將數據取出。 上面的例子中&#xff0c;最…

Redis學習第八課:Redis高級實用特性(一)

Redis高級實用特性 注&#xff1a;我學習的環境是vmware7.1 ubantu10.10 redis 3.0.2 1、安全性 設置客戶端連接后進行任何其他指定前需要的密碼。因為redis速度相當快&#xff0c;一個外部用戶可以在一秒鐘進行很多次的密碼嘗試&#xff0c;這就需要設定非常強大的密碼來防止…

分布式緩存的面試題9

1、面試題 如何保證緩存與數據庫的雙寫一致性&#xff1f; 2、面試官心里分析 你只要用緩存&#xff0c;就可能會涉及到緩存與數據庫雙存儲雙寫&#xff0c;你只要是雙寫&#xff0c;就一定會有數據一致性的問題&#xff0c;那么你如何解決一致性問題&#xff1f; 3、面試題剖析…

ROS與navigation教程——概述

navigation是ROS的二維導航功能包&#xff0c;簡單來說&#xff0c;就是根據輸入的里程計等傳感器的信息流和機器人的全局位置&#xff0c;通過導航算法&#xff0c;計算得出安全可靠的機器人速度控制指令。 代碼庫&#xff1a;https://github.com/ros-planning/navigation 代…

Linux下c開發 之 線程通信與pthread_cond_wait()的使用

pthread_cond_wait() /************pthread_cond_wait()的使用方法**********/pthread_mutex_lock(&qlock); pthread_cond_wait(&qready, &qlock);pthread_mutex_unlock(&qlock);/*****************************************************/The mutex passed …