XUnit 依賴注入

2021-02-20 dotNET跨平臺

XUnit 依賴注入Intro

現在的開發中越來越看重依賴注入的思想,微軟的 Asp.Net Core 框架更是天然集成了依賴注入,那麼在單元測試中如何使用依賴注入呢?

本文主要介紹如何通過 XUnit 來實現依賴注入, XUnit 主要藉助 SharedContext 來共享一部分資源包括這些資源的創建以及釋放。

Scoped

針對 Scoped 的對象可以藉助 XUnit 中的 IClassFixture 來實現

定義自己的 Fixture,需要初始化的資源在構造方法裡初始化,如果需要在測試結束的時候釋放資源需要實現 IDisposable 接口

需要依賴注入的測試類實現接口 IClassFixture<Fixture>

在構造方法中注入實現的 Fixture 對象,並在構造方法中使用 Fixture 對象中暴露的公共成員

Singleton

針對 Singleton 的對象可以藉助 XUnit 中的 ICollectionFixture 來實現

定義自己的 Fixture,需要初始化的資源在構造方法裡初始化,如果需要在測試結束的時候釋放資源需要實現 IDisposable 接口

創建 CollectionDefinition,實現接口 ICollectionFixture<Fixture>,並添加一個 [CollectionDefinition("CollectionName")] Attribute, CollectionName 需要在整個測試中唯一,不能出現重複的 CollectionName

在需要注入的測試類中添加 [Collection("CollectionName")] Attribute,然後在構造方法中注入對應的 Fixture

TipsSamplesScoped Sample

這裡直接以 XUnit 的示例為例:

public class DatabaseFixture : IDisposable

{

public DatabaseFixture()

{

Db = new SqlConnection("MyConnectionString");

// ... initialize data in the test database ...

}

public void Dispose()

{

// ... clean up test data from the database ...

}

public SqlConnection Db { get; private set; }

}

public class MyDatabaseTests : IClassFixture<DatabaseFixture>

{

DatabaseFixture fixture;

public MyDatabaseTests(DatabaseFixture fixture)

{

this.fixture = fixture;

}

[Fact]

public async Task GetTest()

{

// ... write tests, using fixture.Db to get access to the SQL Server ...

// ... 在這裡使用注入 的 DatabaseFixture

}

}

Singleton Sample

這裡以一個對 asp.net core API 的測試為例

自定義 Fixture

/// <summary>

/// Shared Context https://xunit.github.io/docs/shared-context.html

/// </summary>

public class APITestFixture : IDisposable

{

private readonly IWebHost _server;

public IServiceProvider Services { get; }

public HttpClient Client { get; }

public APITestFixture()

{

var baseUrl = $"http://localhost:{GetRandomPort()}";

_server = WebHost.CreateDefaultBuilder()

.UseUrls(baseUrl)

.UseStartup<TestStartup>()

.Build();

_server.Start();

Services = _server.Services;

Client = new HttpClient(new WeihanLi.Common.Http.NoProxyHttpClientHandler())

{

BaseAddress = new Uri($"{baseUrl}")

};

// Add Api-Version Header

// Client.DefaultRequestHeaders.TryAddWithoutValidation("Api-Version", "1.2");

Initialize();

Console.WriteLine("test begin");

}

/// <summary>

/// TestDataInitialize

/// </summary>

private void Initialize()

{

}

public void Dispose()

{

using (var dbContext = Services.GetRequiredService<ReservationDbContext>())

{

if (dbContext.Database.IsInMemory())

{

dbContext.Database.EnsureDeleted();

}

}

Client.Dispose();

_server.Dispose();

Console.WriteLine("test end");

}

private static int GetRandomPort()

{

var random = new Random();

var randomPort = random.Next(10000, 65535);

while (IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners().Any(p => p.Port == randomPort))

{

randomPort = random.Next(10000, 65535);

}

return randomPort;

}

}

[CollectionDefinition("APITestCollection")]

public class APITestCollection : ICollectionFixture<APITestFixture>

{

}

自定義Collection

[CollectionDefinition("TestCollection")]

public class TestCollection : ICollectionFixture<TestStartupFixture>

{

}

自定義一個 TestBase

[Collection("APITestCollection")]

public class ControllerTestBase

{

protected HttpClient Client { get; }

protected IServiceProvider Services { get; }

public ControllerTestBase(APITestFixture fixture)

{

Client = fixture.Client;

Services = fixture.Services;

}

}

需要依賴注入的Test類寫法

public class NoticeControllerTest : ControllerTestBase

{

public NoticeControllerTest(APITestFixture fixture) : base(fixture)

{

}

[Fact]

public async Task GetNoticeList()

{

using (var response = await Client.GetAsync("/api/notice"))

{

Assert.Equal(HttpStatusCode.OK, response.StatusCode);

var responseString = await response.Content.ReadAsStringAsync();

var result = JsonConvert.DeserializeObject<PagedListModel<Notice>>(responseString);

Assert.NotNull(result);

}

}

[Fact]

public async Task GetNoticeDetails()

{

var path = "test-notice";

using (var response = await Client.GetAsync($"/api/notice/{path}"))

{

Assert.Equal(HttpStatusCode.OK, response.StatusCode);

var responseString = await response.Content.ReadAsStringAsync();

var result = JsonConvert.DeserializeObject<Notice>(responseString);

Assert.NotNull(result);

Assert.Equal(path, result.NoticeCustomPath);

}

}

[Fact]

public async Task GetNoticeDetails_NotFound()

{

using (var response = await Client.GetAsync("/api/notice/test-notice1212"))

{

Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);

}

}

}

運行測試,查看我們的 APITestFixture 是不是只實例化了一次,查看輸出日誌:

可以看到我們輸出的日誌只有一次,說明在整個測試過程中確實只實例化了一次,只會啟動一個 web server,確實是單例的

Memo

微軟推薦的是用 Microsoft.AspNetCore.Mvc.Testing 組件去測試 Controller,但是個人感覺不如自己直接去寫web 服務去測試,如果沒必要引入自己不熟悉的組件最好還是不要去引入新的東西,否則可能就真的是踩坑不止了。

Reference

相關焦點

  • 更優雅的在 Xunit 中使用依賴注入
    Xunit.DependencyInjection 7.0 發布了Intro上次我們已經介紹過一次大師的 Xunit.DependencyInjection  在 Xunit 中使用依賴注入 ,最近大師完成了 7.0 的重構並且已經正式發布,已經可以直接安裝使用了7.0 為我們帶來了更好的編程體驗
  • 使用 Xunit.DependencyInjection 改造測試項目
    使用 Xunit.DependencyInjection 改造測試項目Intro這篇文章拖了很長時間沒寫,之前也有介紹過 Xunit.DependencyInjection 這個項目,這個項目是由大師寫的一個 Xunit 基於微軟 GenericHost 和 依賴注入實現的一個擴展庫,可以讓你更方便更容易的在測試項目裡實現依賴注入,而且我覺得另外一點很好的是可以更好的控制操作流程
  • 不到30行代碼實現 golang 依賴注入
    【導讀】golang 做依賴注入有什麼思路和實現方案?本文中作者用例子做了介紹。使用標準庫實現,無額外依賴依賴注入的優勢用java的人對於spring框架一定不會陌生,spring核心就是一個IoC(控制反轉/依賴注入)容器,帶來一個很大的優勢是解耦
  • Go技術日報(2021-02-22)——Go 工程化(三) 依賴注入框架 wire
    三、HHFCodeRv四、MoeLove五、奇伢雲存儲六、網管叨 bi 叨七、mohuishouGo 工程化(三) 依賴注入框架4 HTTP2 相對於 HTTP1.x 有什麼優勢和特點?5 為什麼常說 TCP 有粘包和拆包的問題而不說 UDP ?
  • 開發(三) 依賴注入
    簡單來講,它是使用大型的類型解析容器來解析類的依賴的一種方式。幾乎每一種現代化的程式語言都有很多的與之對應的依賴注入框架。我們重點關注C#語言,它也有很多的依賴注入框架,像是Ninject,AutoFac,Unity( Microsoft),還有很多其他的。
  • 依賴注入與對象間的關係
    依賴注入 vs 創建對象有不少地方這樣描述:「依賴注入改變了使用對象前先創建的傳統方式,而是從外部注入依賴的對象」。應該說這是一種合理的設計,但如果接本段開頭的話說「依賴注入改變了人使用汽車之前先創建的傳統方式」您會不會覺得別捏呢?
  • 依賴注入和控制反轉
    概念 IoC——Inversion of Control  控制反轉 DI——Dependency Injection   依賴注入要想理解上面兩個概念
  • 理解JavaScript中的依賴注入
    隨著我們項目的增長,我們需要依賴的模塊變得越來越多,這個時候,如何有效的組織這些模塊就成了一個非常重要的問題。依賴注入解決的正是如何有效組織代碼依賴模塊的問題。你可能在一些框架或者庫種聽說過「依賴注入」這個詞,比如說著名的前端框架AngularJS,依賴注入就是其中一個非常重要的特性。
  • .net core 依賴注入: 什麼是控制反轉,什麼是依賴注入,什麼是容器, autofac 簡單使用
    綜述ASP.NET Core  支持依賴注入, 也推薦使用依賴注入. 主要作用是用來降低代碼之間的耦合度.
  • WebAPI使用依賴注入
    (點擊上方藍字,可快速關注我們)來源:神牛步行3cnblogs.com/wangrudong003/p/6253486.html本篇將要和大家分享的是webapi中如何使用依賴注入,依賴注入這個東西在接口中常用,實際工作中也用的比較頻繁,因此這裡分享兩種在api中依賴注入的方式Ninject和Unity。
  • 依賴注入和控制反轉的理解
    ●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程式是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙創建及注入依賴對象;為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;哪些方面反轉了?依賴對象的獲取被反轉了。
  • 依賴注入之Autofac使用總結
    今天我想結合實際項目,和正在迷惑中的新手朋友一起來學習和總結依賴注入Autofac的使用和理解。依賴注入粗暴理解依賴: public class A{ public void A(B b) { // do something } }這樣的代碼,估計沒有程序猿不曾使用。
  • 使用Yii2依賴注入簡化開發
    什麼是依賴注入(DI)?對象由框架來創建而不是程式設計師通過 new 創建。
  • Android Hilt依賴注入框架的使用
    中國移動家庭運營中心融合通信 閆同學一、什麼是依賴注入?依賴注入是一個聽起來很「高大上」的概念,但是其實很簡單。本來我要接受各種參數自己構造一個對象,現在只接受一個已經實例化的對象直接作為參數。class A(a:Int,b:Int){ val B = B(a,b)}class A(val b:B)沒錯,依賴注入的概念就是這麼簡單。
  • 深入理解ASP.NET Core 依賴注入
    ,本文從基礎角度理解一下原生DI容器,及介紹下怎麼使用並且如何替換官方提供的默認依賴注入容器。什麼是依賴注入百度百科中對於依賴注入的定義:控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。
  • 深入理解ASP.NET Core依賴注入
    什麼是依賴注入       百度百科中對於依賴注入的定義:控制反轉(Inversion of Control,縮寫為IoC),是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫「依賴查找」(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。
  • 使用 Autofac 進行依賴注入
    突然有一天發現依賴注入這種技能。為了使得架構可測試、易維護、可擴展,需要架構設計為鬆耦合類型,簡單的說也就是解耦。為了解耦前面的人提出各種理論,主要思想是控制反轉,而現在主流的主要是兩個:依賴注入、服務定位(有篇英文文章特意討論這種模式,最終的結論是否定的,乍看了一眼,沒看懂)    有了某個思想便可以在程序中體現,用多了,人們就去對它進行封裝,普通的人封裝的大概就自己用,高人封裝了便成了組件。現在基本上都在弄 .net ,所以說的技術也基本上是這一塊的。.
  • golang依賴注入工具wire指南
    wire與依賴注入 Wire 是一個的Golang依賴注入工具,通過自動生成代碼的方式在編譯期完成依賴注入,Java體系中最出名的Spring框架採用運行時注入,個人認為這是wire和其他依賴注入最大的不同之處。
  • 依賴注入DI和控制反轉IOC的理解
    DI—Dependency Injection,即「依賴注入」:組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升組件重用的頻率,並為系統搭建一個靈活、可擴展的平臺。
  • 我曾想深入了解的:依賴倒置、控制反轉、依賴注入
    這一次,我想深入了解和探討我曾經很迷糊,也沒有一直仔細了解的:依賴倒置、控制反轉、依賴注入 這些概念。什麼是依賴?通常可以理解為一種需要,需求。需要協助才能完成一件事情。static void Main(string[] args) { ILog log = Ioc.GetLogInstance(1); Contract contract = new Contract(log); contract.Successed(); }}依賴注入