在 xunit 測試專案中使用依賴注入

WeihanLi發表於2020-12-27

在 xunit 測試專案中使用依賴注入

Intro

之前寫過幾篇 xunit 依賴注入的文章,今天這篇文章將結合我在 .NET Conf 上的分享,更加系統的分享一下在測試中的應用案例。

之所以想分享這個話題是因為我覺得在我們開發過程中測試是非常重要的一部分,高質量專案的一個重要指標就是測試覆蓋率,同時依賴注入已經是一個現代化應用中不可缺少的一部分,我們的 .NET Core 也是從一開始就整合了依賴注入,依賴注入對於測試專案也是不能缺席的。

xunit 是 .net 裡目前使用的最多的測試元件,Xunit.DependencyInjection 是大師寫的一個 xunit 依賴注入的擴充套件,它是基於微軟的 GenericHost(通用主機) 來實現的,使用它我們可以很輕鬆的實現依賴注入,很好的和 .NET Core 做整合。

How it works

那它是如何工作的呢?我們一起來看一下它的執行流程,它的執行流程分為四步

首先需要構建一個 Host,然後啟動這個 Host,啟動完成後執行測試用例,最後終止這個 Host

執行流程

Host 又是如何構建的呢?我們一起看一下,Host 的構建也是分為四步

第一步,建立一個 HostBuilder,大多數情況下我們不需要用這個方法,使用預設的實現就好

第二步,Host 配置,對 Host 做一些自定義配置

第三步,服務配置,註冊需要的服務

第四步,Configure,可以做一些初始化的配置,比如配置初始化以及測試資料的初始化等

Host構建流程

我們可以在測試專案裡建立一個 Startup 類來控制 Host 的構建過程

示例

接著我們來看一些實際的測試示例,示例分為三部分,首先是一些基本用法,然後是和其他元件的整合,最後是一些擴充套件用法

Get Started

首先來看一下 Startup 的用法,這個 Startup 和 asp.net core 裡的 Startup 是很像的,無論是使用方式上還是實現上都是類似的,有興趣的可以看一下原始碼對比一下,我們來看一下使用方式,通過下面的示例來感受一下

如果你只需要註冊服務,直接在 Startup 中新增一個 ConfigureServices 方法,在這個方法中註冊自己需要的服務即可,和 asp.net core 並無太多不同

如果你需要做一些初始化的工作,可以加一個 Configure 方法,在這個方法中實現自己的初始化邏輯就可以了,如果初始化的時候需要獲取注入的服務例項,直接作為方法引數就可以,類似於 asp.net core 中 Configure 方法,只是不需要配置 Http 請求管道

如果你需要使用的配置,需要使用 Configuration,可以在 ConfigureHost 方法中通過 ConfigureHostConfiguration 擴充套件方法註冊自己的配置

如果需要在註冊服務的時候用到配置,可以在 ConfigureServices 方法中新增一個 HostBuildContext 的引數,HostBuilderContext 中的 Configuration 物件就是在 ConfigureHost 中註冊的配置

如果需要在 Configure 方法中使用配置,直接新增一個 IConfiguration 的方法引數就可以了

我們再來看一下,如何在測試用例中使用注入的服務,一般情況下我們會直接通過構造器注入,在構造方法中新增需要注入的服務即可,除此之外我們還可以通過方法引數注入,結合 InlineDataMemeberData 使用,來看一下這個示例

IoC/AOP Integration

接著我們來看一下和其他元件的整合,AutoFac 是一個很流行的 IOC 元件,AspectCore 是檸檬大佬寫的一個 AOP 框架,我們以這兩個為例子來看一下如何整合第三方的依賴注入和 AOP 元件,前面我們已經提到它是基於微軟的 GenericHost 實現的,而 asp.net core 從 3.0 開始也是基於 GenericHost 實現的,所以在 asp.net core 裡怎麼整合,在這裡也是一樣的,來看一下示例,只需要使用對應的 ServiceProviderFactory 就可以了,是不是很簡單呢

Test Server Integration

然後我們來看一下如何和 TestServer 做整合,TestServer 主要用於整合測試,使用 TestServer 的好處在於它是基於記憶體進行互動的沒有真正的 HTTP 請求和 TCP 連結,會非常的高效,而且也不會監聽某一個埠,所以不會有埠許可權的問題。

TestServer 的使用主要有兩步,首先是服務的註冊,可以使用 IHostBuilderIWebHostBuilderUseTestServer 擴充套件方法註冊 TestServer,可以使用 IHostGetTestClient 擴充套件方法來註冊和 TestServer 進行互動的 HttpClient

服務註冊好之後就可以在測試用例裡通過注入的 HttpClient 請求 API 或頁面了,可以參考這個例子

Extensions

Hosted Service

然後我們來看一些擴充套件用法,IHostedService 可以用來實現一些初始化的操作或者後臺服務,我們可以使用 IHostedService 來實現對應用的 Ready 檢查,應用 Ready 之後再開始執行測試用例,這在有些場景下是很有用的

我們在 k8s 中部署的應用一般都會有一個 HealthCheck/ReadinessCheck 的介面來供 k8s 的 liveness/readiness 探針來探測應用的狀態,只有應用 Ready 之後才會對外部提供服務

這個示例就是一個使用 IHostedService 來實現等待應用 Ready 後再開始執行測試用例的一個 demo

注意:這裡的等待不能在 StartupConfigure 方法中執行,因為 Configure 的執行是在呼叫 Host 的 StartAsync 方法之前執行的,而此時 webServer 還沒有啟動,所以是不能獲取到 TestClient 的,而我們通過 HostedService 就可以在 Web Server 啟動之後再執行我們的等待 Ready 邏輯

ITestOutputHelperAccessor

在測試中如果想要輸出一個日誌的話只能藉助於 ITestOutputHelper 來輸出,直接使用 Console.Write[Line] 是看不到任何輸出的,ITestOutputHelper 只能在測試用例中使用,在測試服務中是不能使用的,Xunit.DependencyInjection 提供了一個 ITestOutputHelperAccessor 的服務,類似於 IHttpContextAccessor,我們可以藉助它來在自定義的服務中獲取 ITestOutputHelper 來輸出日誌

這裡是一個簡單的示例

Logging

再來看一個 OutputHelperAccessor 的實際應用,Xunit.DependencyInjection 提供了一個 Logging 的擴充套件,使得我們可以把測試過程中的日誌輸出出來,更好的幫助我們除錯

整合方式也比較簡單,可以參考這個示例,引用 Xunit.DependencyInjection.Logging 之後,在 LoggerFactory 中註冊 XunitTestOutputLoggerProvider 即可

可以看到我們的日誌直接輸出出來了,預設的日誌級別是 Information ,所以 Debug 級別的日誌沒有輸出出來,有需要的話可以在註冊的時候提供一個委託來控制是否要輸出日誌

Project Template

為了方便大家使用,我們提供了一個專案模板,可以通過一個命令就可以直接建立好一個測試專案,會包含一個預設的 Startup 不再需要自己去寫方法了,使用的時候只需要根據需要做刪減就可以了

預設的 TargetFramework 使用的是 netcoreapp3.1,可以通過 -f/--franework 指定自己想要使用的目標框架,比如說想要生成 net 5.0 的專案只需要指定 -f net5.0 就可以了

生成的內容如下所示:

More

最後列出來了一些可能會有幫助的連結,第一個是專案的原始碼,第二個是 PPT 中所有示例的原始碼,後面的是使用到的 Nuget 包。

這個 xunit 擴充套件的程式碼實現是非常值得學習的,有很多和 asp.net core 的實現是很像的,有需要的可以去看看原始碼學習一下。

希望我的分享對大家有所幫助,大家在使用過程中有遇到任何問題都可以隨時聯絡我或者直接在 Github 上建 issue。

Reference

相關文章