XUnit 依赖注入

XUnit 依赖注入

Intro

现在的开发中越来越看重依赖注入的思想,微软的 Asp.Net Core 框架更是天然集成了依赖注入,那么在单元测试中如何使用依赖注入呢?

本文主要介绍如何通过 XUnit 来实现依赖注入, XUnit 主要借助 SharedContext 来共享一部分资源包括这些资源的创建以及释放。

Scoped

针对 Scoped 的对象可以借助 XUnit 中的 IClassFixture 来实现

  1. 定义自己的 Fixture,需要初始化的资源在构造方法里初始化,如果需要在测试结束的时候释放资源需要实现 IDisposable 接口

  2. 需要依赖注入的测试类实现接口 IClassFixture<Fixture>

  3. 在构造方法中注入实现的 Fixture 对象,并在构造方法中使用 Fixture 对象中暴露的公共成员

Singleton

针对 Singleton 的对象可以借助 XUnit 中的 ICollectionFixture 来实现

  1. 定义自己的 Fixture,需要初始化的资源在构造方法里初始化,如果需要在测试结束的时候释放资源需要实现 IDisposable 接口

  2. 创建 CollectionDefinition,实现接口 ICollectionFixture<Fixture>,并添加一个 [CollectionDefinition("CollectionName")] Attribute, CollectionName 需要在整个测试中唯一,不能出现重复的 CollectionName

  3. 在需要注入的测试类中添加 [Collection("CollectionName")] Attribute,然后在构造方法中注入对应的 Fixture

Tips

  • 如果有多个类需要依赖注入,可以通过一个基类来做,这样就只需要一个基类上添加 [Collection("CollectionName")] Attribute,其他类只需要集成这个基类就可以了

Samples

Scoped 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 的测试为例

  1. 自定义 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>	
{	
}
  1. 自定义Collection

[CollectionDefinition("TestCollection")]	
public class TestCollection : ICollectionFixture<TestStartupFixture>	
{	
}
  1. 自定义一个 TestBase

[Collection("APITestCollection")]	
public class ControllerTestBase	
{	protected HttpClient Client { get; }	protected IServiceProvider Services { get; }	public ControllerTestBase(APITestFixture fixture)	{	Client = fixture.Client;	Services = fixture.Services;	}	
}
  1. 需要依赖注入的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 是不是只实例化了一次,查看输出日志:

640?wx_fmt=png

可以看到我们输出的日志只有一次,说明在整个测试过程中确实只实例化了一次,只会启动一个 web server,确实是单例的

Memo

微软推荐的是用 Microsoft.AspNetCore.Mvc.Testing 组件去测试 Controller,但是个人感觉不如自己直接去写web 服务去测试,如果没必要引入自己不熟悉的组件最好还是不要去引入新的东西,否则可能就真的是踩坑不止了。

Reference

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

  • https://github.com/WeihanLi/ActivityReservation/tree/dev/ActivityReservation.API.Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313868.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

程序员自家种水果,新鲜包邮配送!

点击上面“蓝字”关注我们&#xff01;上次猕猴桃的活动一经推出&#xff0c;得到了广大粉丝的支持&#xff0c;我感到十分欣慰&#xff0c;非常感谢大家对我的信任。好多小伙伴&#xff0c;买了一箱尝过后又下单了好几箱。事实证明&#xff0c;品质才是销量的最佳保证。有些粉…

实现一个简单的基于码云(Gitee) 的 Storage

实现一个简单的基于码云(Gitee) 的 StorageIntro上次在 asp.net core 从单机到集群 一文中提到存储还不支持分布式&#xff0c;并立了一个 flag基于 github 或者 开源中国的码云实现一个 storage于是这两天就来填坑了。。实现了一个简单的基于开源中国的码云的 storage准备工作…

Java线程的6种状态

线程的概念&#xff0c;以及线程的创建方式&#xff0c;见我之前写的博文 本篇文章主要讲Java线程的6种状态 6种状态&#xff1a;初始状态&#xff08;new&#xff09; 、可运行状态&#xff08;Runnable&#xff09;、运行状态&#xff08;Running&#xff09;、阻塞状态&am…

.NET Core 微信小程序支付——(统一下单)

最近公司研发了几个电商小程序&#xff0c;还有一个核心的电商直播&#xff0c;只要是电商一般都会涉及到交易信息&#xff0c;离不开支付系统&#xff0c;这里我们统一实现小程序的支付流程&#xff08;与服务号实现步骤一样&#xff09;。目录1、开通小程序的支付能力2、商户…

用.NET写“算命”程序

前言“算命”&#xff0c;是一种迷信&#xff0c;我父亲那一辈却执迷不悟&#xff0c;有时深陷其中&#xff0c;有时为求一“上上签”&#xff0c;甚至不惜重金&#xff0c;向“天神”保佑。我曾看到过有些算命网站&#xff0c;可以根据人的生辰八字&#xff0c;来求得这个人一…

ASP.NET Core 3.0 迁移避坑指南

一.前言.NET Core 3.0将会在 .NET Conf 大会上正式发布&#xff0c;截止今日发布了9个预览版&#xff0c;改动也是不少&#xff0c;由于没有持续关注&#xff0c;今天将前面开源的动态WebApi项目迁移到.NET Core 3.0还花了不少时间踩坑&#xff0c;给大家分享一下我在迁移过程中…

打不死我的,终将使我强大!DevOps黑客马拉松参赛心得

&#xff08;IDCF DevOps黑客马拉松到底是个啥活动&#xff1f;&#xff09;长得丑活得久、长得帅也惹人爱&#xff01;大家好&#xff0c;我是刘威。隆正信息的业务架构师-花名逸云。非常荣幸可以参加在北京举办的第一届DevOps黑客马拉松比赛。黑客马拉松不是突然冒出来的&…

Java线程池面试题

1、什么是线程池 java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池 多线程技术主要解决处理器单元内多个线程执行的问题&#xff0c;它可以显著减少处理器单元的闲置时间&#xff0c;增加处理器单元的吞吐能力。 假设一个服…

「标签管理」使用标签管理有道云笔记资料

因着大家对文件标签化比较高难道&#xff0c;需要熟悉一个标签工具软件&#xff0c;所以今天暂时来介绍个简单一些的网络资料的标签化管理&#xff0c;使用有道云笔记作为落地工具&#xff0c;同理在OneNote、印象笔记上原理类似。有道云笔记免费功能够用为了选择哪个笔记产品&…

我如何吸引Elastic创始人一起对高并发写入进行优化?

导语&#xff1a;在腾讯金融科技数据应用部的全民 BI 项目里&#xff0c;我们每天面对超过 10 亿级的数据写入&#xff0c;提高 ES 写入性能迫在眉睫&#xff0c;在最近的一次优化中&#xff0c;有幸参与到了 Elasticsearch 开源社区中。背景为了更便捷地分析数据&#xff0c;腾…

微软+开源,那些亲爱的以及热爱的

微软 Reactor 社区空间开幕式暨 Azure Meetup 社区活动已于9月7日在上海圆满结束&#xff01;但是…如何构建一个可持续发展的社区未来的路&#xff0c;仍然很长...你应该知道的微软 Reactor微软 Reactor 是微软为构建开发者社区而提供的一个社区空间&#xff0c;以“予力多元…

ASP.NET Core 2.2 项目升级至 3.0 备忘录

.NET Core 3.0及ASP.NET Core 3.0 前瞻ASP.NET Core 3.0 迁移避坑指南将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中。TargetFramework 改为 netcoreapp3.0 <TargetFramework>netcoreapp3.0</TargetFramework>从 Web 项目&#xff…

Java 死锁

目录&#xff1a; 什么是死锁&#xff1f;死锁是怎么产生的&#xff1f;怎么排查死锁&#xff1f;死锁的预防拓展&#xff1a;Java CPU 100%排查 一 什么是死锁&#xff1f; 注&#xff1a;线程和进程都可能会产生死锁&#xff0c;以下以线程为例 死锁是指两个或两个以上的…

.NET Core 微信小程序退款——(统一退款)

点击上方“dotNET名人堂”&#xff0c;选择“设为星标”用学习的姿态&#xff0c;步入工作的状态继上一篇".NET Core 微信小程序支付——&#xff08;统一下单&#xff09;后"&#xff0c;本文将实现统一退款功能&#xff0c;能支付就应该能退款嘛&#xff0c;一般涉…

Mysql数据库锁机制

一&#xff1a;概念介绍 MySQL数据库锁管理机制&#xff1a; SQL层实现的锁机制    Meta-data元数据锁&#xff1a;在table cache缓存里实现的&#xff0c;为DDL&#xff08;Data Definition Language&#xff09;提供隔离操作。一种特别的meta-data元数据类型&#xff0c;…

干货|亲测有效的N倍学习效果笔记法

这里是Z哥的个人公众号每周五11&#xff1a;45 按时送达当然了&#xff0c;也会时不时加个餐&#xff5e;我的第「108」篇原创敬上大家好&#xff0c;我是Z哥。先祝大家中秋快乐。我猜你现在心情不错&#xff0c;毕竟小长假的第一天才开始&#xff0c;后面还有60个小时的假期&a…

.NET Core 3.0 可卸载程序集原理简析

文章转载授权级别&#xff1a;A 预计阅读时间&#xff1a;8分钟 损失发量&#xff1a;不好统计因为最近在群里被问到如何理解 .NET Core 3.0 可卸载程序集&#xff0c;所以就写了这篇简单的分析。因为时间实在很少&#xff0c;这篇文章只简单的罗列了相关的代码&…

.NetCore技术研究-ConfigurationManager在单元测试下的坑

最近在将原有代码迁移.NET Core, 代码的迁移基本很快&#xff0c;当然也遇到了不少坑&#xff0c;重构了不少&#xff0c;后续逐步总结分享给大家。今天总结分享一下ConfigurationManager遇到的一个问题。先说一下场景&#xff1a;迁移.NET Core后&#xff0c;已有的配置文件&a…

分析一次double强转float的翻车原因

人逢喜事精神爽,总算熬到下班撩~~正准备和同事打个招呼回家,被同事拖住问了.?‍♂️: 你们组做的那块代码,把double类型数据成float有问题啊?.?‍♀️: 嗯?不对是正常啊,float精度是没有double高,但float能保存到小数点后好多位,对我们来说完全够用了!?‍♂️: 不是啊,这不…

.NET Core 3.0之深入源码理解Host(二)

写在前面停了近一个月的技术博客&#xff0c;随着正式脱离996的魔窟&#xff0c;接下来也正式恢复了。本文从源码角度进一步讨论.NET Core 3.0 中关于Host扩展的一些技术点&#xff0c;主要内容是关于创建Long Run Program的创建与守护。关于Host&#xff0c;我们最容易想到的就…