Net6 Xunit 集成测试
对于单元测试、集成测试大部分开发的朋友都懒得去写,因为这要耗费精力去设计去开发,做完项目模块直接postman 调用测试(当然这是一个选择,开发也中经常用到),但是如果测试需要多样化数据,各种场景模拟这样postman测试就暴露了他的局限性,下面我将Net6下没有使用Startup以及NET6以前版本使用Startup的集成测试(单元测试雷同)做一个梳理
1.新建测试项目
2.使用到的类库
Xunti与xunit.runner.visualstudio创建测试项目是自带
Xunit.DependencyInjection 这是一个测试注入的扩展:github地址:https://github.com/pengweiqhca/Xunit.DependencyInjection
Xunit.Extensions.Ordering 这是一个排序执行测试方法的扩展,因为有些方法是需要按照顺序执行,如获取图形验证码-->发送手机验证码-->到获取Token这是一个有序的过程,如果没有按照顺序执行肯定是不对的,github地址:https://github.com/tomaszeman/Xunit.Extensions.Ordering
3.注入
就是将要测试项目的所有注入重新注入测试项目(Program.cs)中和Startup中的所有东东全部注入,NET6中默认取消了Startup类,那么就要手工全部将这些注入再测试项目中添加一次,注入的时候有些是不兼容的做一下小的改动就行,因为https://github.com/pengweiqhca/Xunit.DependencyInjection的注入还是停留在NET5以下版本的,哦对了,还有中间件也是需要在测试项目中添加的,
3.1 NET5 以下,测试项目中的Startup需要自己手工创建,区别在于NET5项目有Startup注入的时候不用手动写很多东西,测试项目直接从项目中的Startup查找注入,
- public class Startup
- {
- // custom host build
- public void ConfigureHost(IHostBuilder hostBuilder)
- {
- hostBuilder
- .ConfigureHostConfiguration(builder =>
- {
- builder.AddJsonFile("appsettings.json", true);
- })
- .ConfigureWebHostDefaults(builder =>
- {
- builder.UseStartup<Dx.H5.Service.Startup>();//此处为项目中的startup,不是测试项目中的startup
- builder.UseTestServer();
- builder.ConfigureServices(services =>
- {
- services.AddSingleton(sp => sp.GetRequiredService<IHost>()
- .GetTestClient()
- );
- });
- })
- ;
- }
- // add services need to injection
- // ConfigureServices(IServiceCollection services)
- // ConfigureServices(IServiceCollection services, HostBuilderContext hostBuilderContext)
- // ConfigureServices(HostBuilderContext hostBuilderContext, IServiceCollection services)
- public void ConfigureServices(IServiceCollection services)
- {
- // ready check
- //services.AddHostedService<ReadyCheckHostedService>();
- }
- public void Configure(ILoggerFactory loggerFactory, ITestOutputHelperAccessor outputHelperAccessor)
- {
- loggerFactory.AddProvider(new XunitTestOutputLoggerProvider(outputHelperAccessor));
- }
- }
3.2 NET6 ,测试项目中的Startup需要自己手工创建,NET6 项目取消了Startup那么就需要手工搬移所有的注入,需要注意的是Startup中的ConfigureServices不支持重载,也就是你只能用一个
ConfigureServices方法,见如下示例,还有测试日志的注入,但是在测试项目中使用ILogger<>好像是有问题的,有时日志不打印, 使用
private readonly ITestOutputHelperAccessor _testOutputHelperAccessor; 替代ILogger<>,直接在测试类构造函数中注入就行
- public class Startup
- {
- const string DefaultCorsPolicyName = "Default";
- public void ConfigureHost(IHostBuilder hostBuilder) =>
- hostBuilder.ConfigureWebHost(webHostBuilder => webHostBuilder
- .UseTestServer()
- .Configure(Configure)
- .UseUrls("http://*:17890","http://*:17880")
- .ConfigureServices(services =>
- {
- services.AddSingleton(sp => sp.GetRequiredService<IHost>()
- .GetTestClient()
- );
- })
- )
- .ConfigureAppConfiguration(lb => lb.AddJsonFile("appsettings.json", false, true));
- public void ConfigureServices(IServiceCollection services, HostBuilderContext context)
- {
- services.AddControllers();
- services.AddEndpointsApiExplorer();
- services.AddSwaggerGen();
- services.AddHttpClient();
- }
- public void Configure(ILoggerFactory loggerFactory, ITestOutputHelperAccessor accessor) =>
- loggerFactory.AddProvider(new XunitTestOutputLoggerProvider(accessor));
- ////public void ConfigureHost(IHostBuilder hostBuilder) { }
- //public void ConfigureServices(IServiceCollection services)
- //{
- //}
- //public IHostBuilder CreateHostBuilder(AssemblyName assemblyName)
- //{
- // return new HostBuilder();
- //}
- private void Configure(IApplicationBuilder app)
- {
- //if (app.Environment.IsDevelopment())
- //{
- // app.UseSwagger();
- // app.UseSwaggerUI();
- //}
- app.UseRouting();
- app.UseAuthorization();
- app.UseEndpoints(endpoints =>
- {
- endpoints
- .MapControllers();
- endpoints.MapGet("/hb/generatetoken", context =>
- {
- return context.Response.WriteAsync(GenerateToken(context));
- });
- });
- }
- string GenerateToken(HttpContext httpContext)
- {
- return "dfdfdfdfd";
- }
- }
- public static IEnumerable<object?[]> ReadFile()
- {
- yield return new object[] { "123"};
- yield return new object[] { "456" };
- }
4.创建测试类测试方法:
需要注意的是api接口测试中url忽略host与端口,默认端口配置请查阅https://github.com/pengweiqhca/Xunit.DependencyInjection文档,UnitTest2中的测试方法是带有数据集合的测试方法,及测试方法是执行多次的,测试方法中的参数数据就是由MemberData(nameof(ReadFile)),其中数据方法ReadFile必须是 public static ReadFile要不然会有报错
- using Microsoft.Extensions.Logging;
- using Newtonsoft.Json;
- using System.Collections.Generic;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Threading.Tasks;
- using Xunit;
- using Xunit.DependencyInjection;
- using Xunit.Extensions.Ordering;
- using static WebApiXunit.Controllers.WeatherForecastController;
- namespace Xunit.WebApiTest
- {
- [Order(1)]
- public class UnitTest1
- {
- private HttpClient _client;
- private ILogger<UnitTest1> _logger;
- //private readonly ITestOutputHelperAccessor _testOutputHelperAccessor;
- public UnitTest1(
- //ITestOutputHelperAccessor testOutputHelperAccessor
- ILogger<UnitTest1> logger,
- HttpClient client
- )
- {
- _logger = logger;
- _client = client;
- }
- [Order(1)]
- [Fact(DisplayName = "1")]
- public async Task Test1()
- {
- var c = new MyClass();
- c.Name = "1";
- c.Description = "e";
- using var request = new HttpRequestMessage(HttpMethod.Post, "WeatherForecast/hb/post/add");
- var content = JsonConvert.SerializeObject(c);
- request.Content = new StringContent(content);
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- var resp = await _client.SendAsync(request);
- //var resp=await _client.GetAsync("/hb/generatetoken");
- _logger.LogInformation("成功");
- if (resp.IsSuccessStatusCode)
- {
- var str = await resp.Content.ReadAsStringAsync();
- Assert.True(true);
- return;
- }
- Assert.True(false);
- }
- [Order(2)]
- [Fact(DisplayName = "2")]
- public async Task Test2()
- {
- var c = new MyClass();
- c.Name = "1";
- c.Description = "e";
- using var request = new HttpRequestMessage(HttpMethod.Post, "WeatherForecast/hb/post/add");
- var content = JsonConvert.SerializeObject(c);
- request.Content = new StringContent(content);
- request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- var resp = await _client.SendAsync(request);
- //var resp=await _client.GetAsync("/hb/generatetoken");
- //_testOutputHelperAccessor.Output.WriteLine("");
- _logger.LogInformation("成功");
- if (resp.IsSuccessStatusCode)
- {
- var str = await resp.Content.ReadAsStringAsync();
- Assert.True(true);
- return;
- }
- Assert.True(false);
- }
- }
- [Order(2)]
- public class UnitTest2
- {
- private HttpClient _client;
- private ILogger<UnitTest2> _logger;
- public UnitTest2(
- ILogger<UnitTest2> logger,
- HttpClient client
- )
- {
- _logger = logger;
- _client = client;
- }
- [Theory]
- [MemberData(nameof(ReadFile))]
- public async Task Test2(string name)
- {
- _logger.LogInformation(name);
- Assert.True(true);
- }
- public static IEnumerable<object?[]> ReadFile()
- {
- yield return new object[] { "123"};
- yield return new object[] { "456" };
- }
- }
- }
5.排序执行测试方法:
使用Xunit.Extensions.Ordering进行排序执行测试方法时:首先在测试项目中新建一个AssemblyInfo.cs加入如下内容,主要没有类名及命名空间,其中[assembly: TestFramework("Xunit.Extensions.Ordering.TestFramework", "Xunit.Extensions.Ordering")]是一个按照集合进行排序的使用,但是他与已有xunit assembly冲突,暂时么有找到解决方法,所以该排序功能暂时不支持,类中的[Order(2)]为第一优先级排序顺序,方法中的[Order(2)]即在类的顺序下再排序
using Xunit;
using Xunit.Extensions.Ordering;
[assembly: CollectionBehavior(DisableTestParallelization = true)]
//[assembly: TestFramework("Xunit.Extensions.Ordering.TestFramework", "Xunit.Extensions.Ordering")]
[assembly: TestCaseOrderer("Xunit.Extensions.Ordering.TestCaseOrderer", "Xunit.Extensions.Ordering")]
[assembly: TestCollectionOrderer("Xunit.Extensions.Ordering.CollectionOrderer", "Xunit.Extensions.Ordering")]
- using Xunit;
- using Xunit.Extensions.Ordering;
- [assembly: CollectionBehavior(DisableTestParallelization = true)]
- //[assembly: TestFramework("Xunit.Extensions.Ordering.TestFramework", "Xunit.Extensions.Ordering")]
- [assembly: TestCaseOrderer("Xunit.Extensions.Ordering.TestCaseOrderer", "Xunit.Extensions.Ordering")]
- [assembly: TestCollectionOrderer("Xunit.Extensions.Ordering.CollectionOrderer", "Xunit.Extensions.Ordering")]
6.运行测试项目:
在vs中运行测试项目中右键可以看到运行和调试测试项目运行比较简单,如果在服务器上需要使用dotnet test运行测试,注意配置文件要与服务器的匹配,将项目整体目录拷贝到服务器,cd 到测试项目目录下执行 dotnet test,有多少个接口瞬间测试完毕,而且在项目后续迭代更新的时候,只需要执行以下就可以测试所有的接口。
Net6 Xunit 集成测试的更多相关文章
- .net core + xunit 集成测试
xunit地址:https://github.com/xunit/xunit 一.利用请求来测试接口,主要是测试webapi控制器方法 ①添加xunit项目 ,然后引用我们的主项目,nuget: Mi ...
- 如何在ASP.NET 5和XUnit.NET中进行LocalDB集成测试
今天继续昨天的话题--单元测试,不过是在ASP.NET 5中的单元测试. 在当前的Visual Studio 2015 CTP6中,MSTest是不支持对ASP.NET 5项目进行单元测试的.因而,要 ...
- Asp.Net Core 轻松学-利用xUnit进行主机级别的网络集成测试
前言 在开发 Asp.Net Core 应用程序的过程中,我们常常需要对业务代码编写单元测试,这种方法既快速又有效,利用单元测试做代码覆盖测试,也是非常必要的事情:但是,但我们需要对系统进行集 ...
- 体验 ASP.NET Core 集成测试三剑客:xUnit.net、TestServer、EF Core InMemory
这是昨天解决的一个问题,针对一个 web api 的客户端代理类写集成测试,既要测试 web api,又要测试 web api 客户端. 测试 web api,就要在运行测试时自动启动 web api ...
- .netcore持续集成测试篇之开篇简介及Xunit基本使用
系列目录 为了支持跨平台,微软为.net平台提供了.net core test sdk,这样第三方测试框架诸如Nunit,Xunit等只需要按照sdk提供的api规范进行开发便可以被dotnet cl ...
- 使用xunit对asp.net core webapi进行集成测试
新项目我们采用前后端分离,后端采用asp.net core webapi, 如何对后端代码进行自动化测试呢,有以下几种方案: 1. 单元测试,目前这个方案对我们来说难度很大,抛开时间的问题,单元测试对 ...
- .netcore持续集成测试篇之Xunit数据驱动测试一
系列目录 Nunit里提供了丰富的数据测试功能,虽然Xunit里提供的比较少,但是也能满足很多场景下使用了,如果数据场景非常复杂,Nunit和Xunit都是无法胜任的,有不少测试者选择自己编写一个数据 ...
- .netcore持续集成测试篇之Xunit结合netcore内存服务器发送post请求
系列目录 Web项目中,很多与用户数据交互的请求都是Post请求,想必大家都用过HttpClient构造过post请求,这里并不对HttpClient做详细介绍,只介绍一些常用的功能.并结合AutoF ...
- ASP.NET Core 中文文档 第五章 测试(5.2)集成测试
原文: Integration Testing 作者: Steve Smith 翻译: 王健 校对: 孟帅洋(书缘) 集成测试确保应用程序的组件组装在一起时正常工作. ASP.NET Core支持使用 ...
随机推荐
- Cadence 错误合集
1.原理图DRC出现如下错误"Duplicate Pin Name "GND" found on Packag" 解决方案:原因是元件引脚重复定义,可以进行重新 ...
- 《css揭秘》读书笔记
第一章 引言 css编码技巧 在引言中,作者提到使用em与inherit来实现css代码的简洁与可维护性.但是根据本司机两年的开发经验来看,在实际开发中很少来使用em这个单位.如何用以及何时去使用,还 ...
- 检查浏览器支持Webp
什么是Webp? Webp 是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8.根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即使这 ...
- ES6-11学习笔记--代理Proxy
Proxy代理 常用拦截方法 ES5拦截: let obj = {} let newVal = '' Object.defineProperty(obj, 'name', { get() { cons ...
- Android:setOnItemClickListener cannot be used with a spinner报错
错误原因: Spinner对象不支持使用setOnItemClickListener方法监听点击事项 解决方法: 使用setOnItemSelectedListener方法代替setOnItemCli ...
- 接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理)
接口和抽象类的区别(不讲废话,干货满满,JDK1.8最新整理) 1.抽象类 以下说辞可能不太准确,但是会让你醍醐灌顶 抽象类是把一些具有共同属性(包括行为)的东西抽象出来,比如: 小狗有身高,体重,颜 ...
- OllyDbg---call和ret指令
call和ret call指令 cal指令是转移到指定的子程序处,后面紧跟的操作数就是给定的地址. 例如,call 401362表示转移到地址401362处,调用401362处的子程序,当子程序调用完 ...
- Struts2-从值栈获取list集合数据(三种方式)
创建User封装数据类 public class User { private String username; private String password; public String getP ...
- Go 框架学习之旅 ① 深入解析 net/http 启动服务的层级逻辑
Web Server. net/http 标准库怎么学. 库函数. 结构定义. 结构函数. 思维导图解析HTTP服务端. 层级逻辑. 创建框架的Server结构. OSI参考模型. TCP/IP五层模 ...
- EMC信号完整性落地实测1---走出玄学
EMC信号完整性落地实测1---走出玄学 无论我们从51单片机,STM32电路,运放,传感器,ADC采集还是可控硅晶闸管等等电源电路跨入到电子工程师的行业,我们通常会长时间处于低频的电子电路设计调试阶 ...