关于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 staticReadFile要不然会有报错
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 集成测试的文章就介绍到这了,更多相关Net6 Xunit 集成测试内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!