關(guān)于Net6?Xunit?集成測(cè)試的問(wèn)題
對(duì)于單元測(cè)試、集成測(cè)試大部分開(kāi)發(fā)的朋友都懶得去寫,因?yàn)檫@要耗費(fèi)精力去設(shè)計(jì)去開(kāi)發(fā),做完項(xiàng)目模塊直接postman 調(diào)用測(cè)試(當(dāng)然這是一個(gè)選擇,開(kāi)發(fā)也中經(jīng)常用到),但是如果測(cè)試需要多樣化數(shù)據(jù),各種場(chǎng)景模擬這樣postman測(cè)試就暴露了他的局限性,下面我將Net6下沒(méi)有使用Startup以及NET6以前版本使用Startup的集成測(cè)試(單元測(cè)試?yán)淄┳鲆粋€(gè)梳理
1.新建測(cè)試項(xiàng)目
2.使用到的類庫(kù)
Xunti與xunit.runner.visualstudio創(chuàng)建測(cè)試項(xiàng)目是自帶
Xunit.DependencyInjection 這是一個(gè)測(cè)試注入的擴(kuò)展:github地址:https://github.com/pengweiqhca/Xunit.DependencyInjection
Xunit.Extensions.Ordering 這是一個(gè)排序執(zhí)行測(cè)試方法的擴(kuò)展,因?yàn)橛行┓椒ㄊ切枰凑枕樞驁?zhí)行,如獲取圖形驗(yàn)證碼-->發(fā)送手機(jī)驗(yàn)證碼-->到獲取Token這是一個(gè)有序的過(guò)程,如果沒(méi)有按照順序執(zhí)行肯定是不對(duì)的,github地址:https://github.com/tomaszeman/Xunit.Extensions.Ordering
3.注入
就是將要測(cè)試項(xiàng)目的所有注入重新注入測(cè)試項(xiàng)目(Program.cs)中和Startup中的所有東東全部注入,NET6中默認(rèn)取消了Startup類,那么就要手工全部將這些注入再測(cè)試項(xiàng)目中添加一次,注入的時(shí)候有些是不兼容的做一下小的改動(dòng)就行,因?yàn)?a target="_blank">https://github.com/pengweiqhca/Xunit.DependencyInjection的注入還是停留在NET5以下版本的,哦對(duì)了,還有中間件也是需要在測(cè)試項(xiàng)目中添加的,
3.1 NET5 以下,測(cè)試項(xiàng)目中的Startup需要自己手工創(chuàng)建,區(qū)別在于NET5項(xiàng)目有Startup注入的時(shí)候不用手動(dòng)寫很多東西,測(cè)試項(xiàng)目直接從項(xiàng)目中的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>();//此處為項(xiàng)目中的startup,不是測(cè)試項(xiàng)目中的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 ,測(cè)試項(xiàng)目中的Startup需要自己手工創(chuàng)建,NET6 項(xiàng)目取消了Startup那么就需要手工搬移所有的注入,需要注意的是Startup中的ConfigureServices不支持重載,也就是你只能用一個(gè)ConfigureServices方法,見(jiàn)如下示例,還有測(cè)試日志的注入,但是在測(cè)試項(xiàng)目中使用ILogger<>好像是有問(wèn)題的,有時(shí)日志不打印, 使用private readonly ITestOutputHelperAccessor _testOutputHelperAccessor; 替代ILogger<>,直接在測(cè)試類構(gòu)造函數(shù)中注入就行
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.創(chuàng)建測(cè)試類測(cè)試方法:
需要注意的是api接口測(cè)試中url忽略host與端口,默認(rèn)端口配置請(qǐng)查閱https://github.com/pengweiqhca/Xunit.DependencyInjection文檔,UnitTest2中的測(cè)試方法是帶有數(shù)據(jù)集合的測(cè)試方法,及測(cè)試方法是執(zhí)行多次的,測(cè)試方法中的參數(shù)數(shù)據(jù)就是由MemberData(nameof(ReadFile)),其中數(shù)據(jù)方法ReadFile必須是 public staticReadFile要不然會(huì)有報(bào)錯(cuò)
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.排序執(zhí)行測(cè)試方法:
使用Xunit.Extensions.Ordering進(jìn)行排序執(zhí)行測(cè)試方法時(shí):首先在測(cè)試項(xiàng)目中新建一個(gè)AssemblyInfo.cs加入如下內(nèi)容,主要沒(méi)有類名及命名空間,其中[assembly: TestFramework("Xunit.Extensions.Ordering.TestFramework", "Xunit.Extensions.Ordering")]是一個(gè)按照集合進(jìn)行排序的使用,但是他與已有xunit assembly沖突,暫時(shí)么有找到解決方法,所以該排序功能暫時(shí)不支持,類中的[Order(2)]為第一優(yōu)先級(jí)排序順序,方法中的[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.運(yùn)行測(cè)試項(xiàng)目:
在vs中運(yùn)行測(cè)試項(xiàng)目中右鍵可以看到運(yùn)行和調(diào)試測(cè)試項(xiàng)目運(yùn)行比較簡(jiǎn)單,如果在服務(wù)器上需要使用dotnet test運(yùn)行測(cè)試,注意配置文件要與服務(wù)器的匹配,將項(xiàng)目整體目錄拷貝到服務(wù)器,cd 到測(cè)試項(xiàng)目目錄下執(zhí)行 dotnet test,有多少個(gè)接口瞬間測(cè)試完畢,而且在項(xiàng)目后續(xù)迭代更新的時(shí)候,只需要執(zhí)行以下就可以測(cè)試所有的接口。
到此這篇關(guān)于Net6 Xunit 集成測(cè)試的文章就介紹到這了,更多相關(guān)Net6 Xunit 集成測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
簡(jiǎn)單好用的ASP.NET分頁(yè)類(支持AJAX、自定義文字)
這篇文章主要介紹了簡(jiǎn)單好用的ASP.NET分頁(yè)類(支持AJAX、自定義文字),本文直接給出實(shí)現(xiàn)代碼和使用方法,需要的朋友可以參考下2015-06-06詳解.NET Core+Docker 開(kāi)發(fā)微服務(wù)
這篇文章給大家分享了.NET Core+Docker 開(kāi)發(fā)微服務(wù)的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們參考下。2018-09-09Asp.net實(shí)時(shí)顯示文本框字?jǐn)?shù)實(shí)現(xiàn)代碼
實(shí)時(shí)顯示文本框字?jǐn)?shù)在日常開(kāi)發(fā)中很常見(jiàn),也很實(shí)用,接下來(lái)為大家介紹下如何實(shí)現(xiàn)實(shí)時(shí)顯示,感興趣的朋友可以參考下哈,希望可以幫助到你2013-04-04asp.net下生成99個(gè)不同的隨機(jī)數(shù)
asp.net下生成99個(gè)不同的隨機(jī)數(shù)...2007-04-04.NET使用YARP根據(jù)域名轉(zhuǎn)發(fā)實(shí)現(xiàn)反向代理
這篇文章介紹了.NET使用YARP根據(jù)域名轉(zhuǎn)發(fā)實(shí)現(xiàn)反向代理的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09asp.net ListView交替背景顏色實(shí)現(xiàn)代碼
在asp.net中ListView的交替背景顏色實(shí)現(xiàn),GridView的處理得較多,ListView可以這樣實(shí)現(xiàn)。2010-02-02一個(gè)伴隨ASP.NET從1.0到4.0的OutputCache Bug介紹
一個(gè)伴隨ASP.NET從1.0到4.0的OutputCache Bug介紹,學(xué)習(xí).net的朋友可以參考下。2011-11-11.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First
這篇文章主要介紹了.Net極限生產(chǎn)力之分表分庫(kù)全自動(dòng)化Migrations?Code-First,輕量級(jí)針對(duì)分表分庫(kù)讀寫分離的解決方案,具有零依賴、零學(xué)習(xí)成本、零業(yè)務(wù)代碼入侵適配2022-07-07