1. 程式人生 > >.NET Core TDD 前傳: 編寫易於測試的程式碼 -- 全域性狀態

.NET Core TDD 前傳: 編寫易於測試的程式碼 -- 全域性狀態

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

本文是第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使用即可.

相關推薦

.NET Core TDD : 編寫易於測試的代碼 -- 縫

廠商 關鍵字 com omap 註入 大堆 解決 而是 不知道 有時候不是我們不想做單元測試, 而是這代碼寫的實在是沒法測試.... 舉個例子, 如果一輛汽車在產出後沒完成測試, 那麽沒人敢去駕駛它. 代碼也是一樣的, 如果項目未能進行該做的測試, 那麽客戶就不敢去使用它

.NET Core TDD : 編寫易於測試的代碼 -- 構建對象

rep 文章 建立 ini 代碼 ali 請求 uid 依賴項 該系列第1篇: 講述了如何創造"縫". "縫"(seam)是需要知道的概念. 本文是第2篇, 介紹的是如何避免在構建對象時寫出不易測試的代碼. 本文的概念性內容大部分都來自Misko Hevery的這篇博客

.NET Core TDD : 編寫易於測試程式碼 -- 構建物件

該系列第1篇: 講述了如何創造"縫".  "縫"(seam)是需要知道的概念. 本文是第2篇, 介紹的是如何避免在構建物件時寫出不易測試的程式碼. 本文的概念性內容大部分都來自Misko Hevery的這篇部落格文章. 構建 還是用上文裡汽車的例子. 通常情況下, 我們是先去建造汽車, 組裝好汽車後,

.NET Core TDD : 編寫易於測試程式碼 -- 縫

有時候不是我們不想做單元測試, 而是這程式碼寫的實在是沒法測試.... 舉個例子, 如果一輛汽車在產出後沒完成測試, 那麼沒人敢去駕駛它. 程式碼也是一樣的, 如果專案未能進行該做的測試, 那麼客戶就不敢去使用它, 即使使用了也會遇到“車禍”.  為什麼要測試/測試的好處 它可以儘早發現bug,

.NET Core TDD : 編寫易於測試程式碼 -- 單一職責

第1篇: 講述了如何創造"縫".  "縫"(seam)是需要知道的概念. 本文是第5篇, 也是最後一篇, 介紹的是單一職責 類做了太多的工作 例子, 某軟體公司, 原有專案開發, 測試, 售前, 售後, 財務等員工. 後來由於公司沒錢, 裁掉了測試, 讓開發兼職; 過了段時間, 又裁掉了

.NET Core TDD : 編寫易於測試程式碼 -- 全域性狀態

第1篇: 講述了如何創造"縫".  "縫"(seam)是需要知道的概念. 本文是第4篇, 將介紹全域性狀態引起的問題. 全域性狀態 全域性狀態, 也可以叫做應用程式狀態, 它是一組變數, 這些變數維護著應用程式的高階狀態. 在程式裡, 全域性狀態可能都存放在一個全域性狀態物件裡, 例如AS

.NET Core TDD : 編寫易於測試程式碼 -- 依賴項

第1篇: 講述了如何創造"縫".  "縫"(seam)是需要知道的概念. 本文是第3篇, 講述依賴項和迪米特法則. 迪米特法則 (Law of Demeter) 還是使用建造汽車的例子. 生產汽車的時候需要輪胎, 組裝時需要什麼型號的輪胎, 就請求該型號的輪胎, 然後相關人員會從庫房把該型號的輪

.NET Core TDD OA 信用盤平臺搭建: 編寫易於測試的代碼構建對象

建造者 五個 包括 值類型 bject 方法 之前 關系 服務 聯系方式:QQ:2747044651 網址http://zhengtuwl.com常情況下, 我們是先去建造汽車, 組裝好汽車後, 我們再去駕駛它. 軟件開發也類似, 我們應該把對象構造完畢之後, 再去用它.

使用Bitbucket Pipeline進行.Net Core項目的自動構建、測試和部署

net yml cimage 參考 www 模板 -c 免費 clas 1. 引言 首先,Bitbucket提供支持Mercurial和Git版本控制系統的網絡托管服務。簡單來說,它類似於GitHub,不同之處在於它支持個人免費創建私有項目倉庫。除此之外,Bitbucke

輕松掌握VS Code開發.Net Core及創建Xunit單元測試

blog logs 寫文章 編譯 分享 單獨 etc 2.0 ren 前言 本篇文章主要還是介紹使用 VS Code 進行.Net Core開發和常用 CLI命令的使用,至於為啥要用VS Code ,因為它是真的是好看又好用 :) ,哈哈,主要還是為了跨平臺開發做準備。 開

學習 ASP.NET Core 2.1:集成測試中使用 WebApplicationFactory

UNC enc sta 測試 修改 構造 creat -a msdn WebApplicationFactory 是 ASP.NET Core 2.1 新特性 MVC functional test infrastructure 中帶來的新東東,它封裝了 TestServe

ASP.NET Core 入門教程 5、ASP.NET Core MVC 檢視值入門

一、前言 1、本教程主要內容 ASP.NET Core MVC 檢視引擎(Razor)簡介 ASP.NET Core MVC 檢視(Razor)ViewData使用示例 ASP.NET Core MVC 檢視(Razor)ViewBag使用示例 ASP.NET Core NVC 檢視(Razor)強型別傳值

ASP NET Core檔案上與下載 多種上方式

                                                                                                    前言前段時間專案上線,實在太忙,最近終於開始可以研究研究ASP.NET Core了.打算寫個系列,但是還沒想好

ASP.NET Core檔案上與下載(多種上方式)

前言前段時間專案上線,實在太忙,最近終於開始可以研究研究ASP.NET Core了.打算寫個系列

.Net Core 檔案上與下載

參考連結: 遇到的問題: 按 參考1 中測試,下載檔案檔名總是變成方法名(DownloadFile),並且沒有副檔名,儲存後改副檔名可正常檢視。 參考 連結3 測試無效,未解決下載問題。 參考 連結2,問題解決。 程式碼實現如下: 檔案上傳 [

.NET CORE與Spring Boot編寫控制檯程式應有的優雅姿勢

本文分別說明.NET CORE與Spring Boot 編寫控制檯程式應有的“正確”方法,以便.NET程式設計師、JAVA程式設計師可以相互學習與加深瞭解,注意本文只介紹用法,不會刻意強調哪種語言或哪種框架寫的控制檯程式要好。 本文所說的編寫控制檯程式應有的“正

.NetCore技術研究-.NET Core遷移的準備工作

前段時間遷移.NET Core做了大量的試水和評估,今天整理一下分享給大家。大致有以下幾個部分: 1. .NET Core的由來 2. 為什麼要遷移.NET Core 3. .NET Core3.X主要特性 4. .NET Standard和.NET Core 5. .NET Core Roadma

asp.net core 使用 TestServer 來做整合測試

# asp.net core 使用 TestServer 來做整合測試 ## Intro 之前我的專案裡的整合測試是隨機一個埠,每次都真實的啟動一個 WebServer,之前也有看到過微軟文件上 `TestServer` 的介紹,當時沒仔細看過以為差不多就沒用,一直是啟動了一個真正的 WebServer

如何編寫單元測試程式碼

                                         如何編寫

如何編寫Junit測試程式碼

package com.snt.aaa.config.service.impl; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans