ASP.NET?Core項(xiàng)目使用xUnit進(jìn)行單元測(cè)試
一、前言
在以前的.NET Framework項(xiàng)目中,我們也寫過(guò)一些單元測(cè)試的項(xiàng)目,而在ASP.NET Core 這種Web或者API應(yīng)用程序中要做單元測(cè)試是很方便的。
這篇文章主要講解如何使用xUnit對(duì)ASP.NET Core應(yīng)用程序做單元測(cè)試。.NET Core中常用的測(cè)試工具還有NUnit和MSTest。
xUnit是一個(gè)測(cè)試框架,可以針對(duì).net/.net core項(xiàng)目進(jìn)行測(cè)試。測(cè)試項(xiàng)目需要引用被測(cè)試的項(xiàng)目,從而對(duì)其進(jìn)行測(cè)試。測(cè)試項(xiàng)目同時(shí)需要引用xUnit庫(kù)。測(cè)試編寫好后,用Test Runner來(lái)運(yùn)行測(cè)試。Test Runner可以讀取測(cè)試代碼,并且會(huì)知道我們所使用的測(cè)試框架,然后執(zhí)行,并顯示結(jié)果。目前可用的Test Runner包括vs自帶的Test Explorer,或者dotnet core命令行,以及第三方工具,例如resharper等。
xUnit可以支持多種平臺(tái)的測(cè)試:
- .NET Framework
- .NET Core
- .NET Standard
- UWP
- Xamarin
二、創(chuàng)建示例項(xiàng)目
為了使示例項(xiàng)目更加的貼近真實(shí)的項(xiàng)目開發(fā),這里采用分層的方式創(chuàng)建一個(gè)示例項(xiàng)目,創(chuàng)建完成后的項(xiàng)目結(jié)構(gòu)如下圖所示:
下面講解一下每層的作用,按照從上往下的順序:
- TestDemo:從名字就可以看出來(lái),這是一個(gè)單元測(cè)試的項(xiàng)目,針對(duì)控制器進(jìn)行測(cè)試。
- UnitTest.Data:數(shù)據(jù)訪問,封裝與EntityFrameworkCore相關(guān)的操作。
- UnitTest.IRepository:泛型倉(cāng)儲(chǔ)接口,封裝基礎(chǔ)的增刪改查。
- UnitTest.Model:實(shí)體層,定義項(xiàng)目中使用到的所有實(shí)體。
- UnitTest.Repository:泛型倉(cāng)儲(chǔ)接口實(shí)現(xiàn)層,實(shí)現(xiàn)接口里面定義的方法。
- UnitTestDemo:ASP.NET Core WebApi,提供API接口。
1、UnitTest.Model
實(shí)體層里面只有一個(gè)Student類:
using System; using System.Collections.Generic; using System.Text; namespace UnitTest.Model { public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } } }
2、UnitTest.Data
里面封裝與EF Core有關(guān)的操作,首先需要引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三個(gè)NuGet包,直接在管理NuGet程序包里面引入,這里不在講述。
引入相關(guān)NuGet包以后,我們創(chuàng)建數(shù)據(jù)上下文類,該類繼承自EF Core的DbContext,里面設(shè)置表名和一些屬性:
using Microsoft.EntityFrameworkCore; using UnitTest.Model; namespace UnitTest.Data { /// <summary> /// 數(shù)據(jù)上下文類 /// </summary> public class AppDbContext : DbContext { /// <summary> /// 通過(guò)構(gòu)造函數(shù)給父類構(gòu)造傳參 /// </summary> /// <param name="options"></param> public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Student> Students { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Student>().ToTable("T_Student"); modelBuilder.Entity<Student>().HasKey(p => p.ID); modelBuilder.Entity<Student>().Property(p => p.Name).HasMaxLength(32); // 添加種子數(shù)據(jù) modelBuilder.Entity<Student>().HasData( new Student() { ID = 1, Name = "測(cè)試1", Age = 20, Gender = "男" }, new Student() { ID = 2, Name = "測(cè)試2", Age = 22, Gender = "女" }, new Student() { ID = 3, Name = "測(cè)試3", Age = 23, Gender = "男" }); base.OnModelCreating(modelBuilder); } } }
這里采用數(shù)據(jù)遷移的方式生成數(shù)據(jù)庫(kù),需要在API項(xiàng)目中引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三個(gè)NuGet包。引入方式同上。
然后在API項(xiàng)目的appsettings.json文件里面添加數(shù)據(jù)庫(kù)鏈接字符串:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", // 數(shù)據(jù)庫(kù)連接字符串 "ConnectionString": { "DbConnection": "Initial Catalog=TestDb;User Id=sa;Password=1234;Data Source=.;Connection Timeout=10;" } }
在JSON文件中添加完連接字符串以后,修改Startup類的ConfigureServices方法,在里面配置使用在json文件中添加的連接字符串:
// 添加數(shù)據(jù)庫(kù)連接字符串 services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value); });
這樣就可以使用數(shù)據(jù)遷移的方式生成數(shù)據(jù)庫(kù)了。
3、UnitTest.IRepository
該項(xiàng)目中使用泛型倉(cāng)儲(chǔ),定義一個(gè)泛型倉(cāng)儲(chǔ)接口:
using System.Collections.Generic; using System.Threading.Tasks; namespace UnitTest.IRepository { public interface IRepository<T> where T:class,new() { Task<List<T>> GetList(); Task<int?> Add(T entity); Task<int?> Update(T entity); Task<int?> Delete(T entity); } }
然后在定義IStudentRepository接口繼承自IRepository泛型接口:
using UnitTest.Model; namespace UnitTest.IRepository { public interface IStudentRepository: IRepository<Student> { } }
4、UnitTest.Repository
這里是實(shí)現(xiàn)上面定義的倉(cāng)儲(chǔ)接口:
using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UnitTest.Data; using UnitTest.IRepository; using UnitTest.Model; namespace UnitTest.Repository { public class StudentRepository : IStudentRepository { private readonly AppDbContext _dbContext; /// <summary> /// 通過(guò)構(gòu)造函數(shù)實(shí)現(xiàn)依賴注入 /// </summary> /// <param name="dbContext"></param> public StudentRepository(AppDbContext dbContext) { _dbContext = dbContext; } public async Task<int?> Add(Student entity) { _dbContext.Students.Add(entity); return await _dbContext.SaveChangesAsync(); } public async Task<int?> Delete(Student entity) { _dbContext.Students.Remove(entity); return await _dbContext.SaveChangesAsync(); } public async Task<List<Student>> GetList() { List<Student> list = new List<Student>(); list = await Task.Run<List<Student>>(() => { return _dbContext.Students.ToList(); }); return list; } public async Task<int?> Update(Student entity) { Student student = _dbContext.Students.Find(entity.ID); if (student != null) { student.Name = entity.Name; student.Age = entity.Age; student.Gender = entity.Gender; _dbContext.Entry<Student>(student).State = Microsoft.EntityFrameworkCore.EntityState.Modified; return await _dbContext.SaveChangesAsync(); } return 0; } } }
5、UnitTestDemo
先添加一個(gè)Value控制器,里面只有一個(gè)Get方法,而且沒有任何的依賴關(guān)系,先進(jìn)行最簡(jiǎn)單的測(cè)試:
using Microsoft.AspNetCore.Mvc; namespace UnitTestDemo.Controllers { [Route("api/[controller]")] [ApiController] public class ValueController : ControllerBase { [HttpGet("{id}")] public ActionResult<string> Get(int id) { return $"Para is {id}"; } } }
6、TestDemo
我們?cè)谔砑訙y(cè)試項(xiàng)目的時(shí)候,直接選擇使用xUnit測(cè)試項(xiàng)目,如下圖所示:
這樣項(xiàng)目創(chuàng)建完成以后,就會(huì)自動(dòng)添加xUnit的引用:
<ItemGroup> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> </ItemGroup>
但要測(cè)試 ASP.NET Core 應(yīng)用還需要添加兩個(gè) NuGet 包:
Install-Package Microsoft.AspNetCore.App Install-Package Microsoft.AspNetCore.TestHost
上面是使用命令的方式進(jìn)行安裝,也可以在管理NuGet程序包里面進(jìn)行搜索,然后安裝。
千萬(wàn)不要忘記還要引入要測(cè)試的項(xiàng)目。最后的項(xiàng)目引入是這樣的:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.2" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" /> <PackageReference Include="coverlet.collector" Version="1.0.1" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\UnitTest.Model\UnitTest.Model.csproj" /> <ProjectReference Include="..\UnitTestDemo\UnitTestDemo.csproj" /> </ItemGroup> </Project>
都添加完以后,重新編譯項(xiàng)目,保證生成沒有錯(cuò)誤。
三、編寫單元測(cè)試
單元測(cè)試按照從上往下的順序,一般分為三個(gè)階段:
- Arrange:準(zhǔn)備階段。這個(gè)階段做一些準(zhǔn)備工作,例如創(chuàng)建對(duì)象實(shí)例,初始化數(shù)據(jù)等。
- Act:行為階段。這個(gè)階段是用準(zhǔn)備好的數(shù)據(jù)去調(diào)用要測(cè)試的方法。
- Assert:斷定階段。這個(gè)階段就是把調(diào)用目標(biāo)方法的返回值和預(yù)期的值進(jìn)行比較,如果和預(yù)期值一致則測(cè)試通過(guò),否則測(cè)試失敗。
我們?cè)贏PI項(xiàng)目中添加了一個(gè)Value控制器,我們以Get方法作為測(cè)試目標(biāo)。一般一個(gè)單元測(cè)試方法就是一個(gè)測(cè)試用例。
我們?cè)跍y(cè)試項(xiàng)目中添加一個(gè)ValueTest測(cè)試類,然后編寫一個(gè)單元測(cè)試方法,這里是采用模擬HTTPClient發(fā)送Http請(qǐng)求的方式進(jìn)行測(cè)試:
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.Net; using System.Net.Http; using System.Threading.Tasks; using UnitTestDemo; using Xunit; namespace TestDemo { public class ValueTests { public HttpClient _client { get; } /// <summary> /// 構(gòu)造方法 /// </summary> public ValueTests() { var server = new TestServer(WebHost.CreateDefaultBuilder() .UseStartup<Startup>()); _client = server.CreateClient(); } [Fact] public async Task GetById_ShouldBe_Ok() { // 1、Arrange var id = 1; // 2、Act // 調(diào)用異步的Get方法 var response = await _client.GetAsync($"/api/value/{id}"); // 3、Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } }
我們?cè)跇?gòu)造函數(shù)中,通過(guò)TestServer拿到一個(gè)HttpClient對(duì)象,用它來(lái)模擬Http請(qǐng)求。我們寫了一個(gè)測(cè)試用例,完整演示了單元測(cè)試的Arrange、Act和Assert三個(gè)步驟。
1、運(yùn)行單元測(cè)試
單元測(cè)試用例寫好以后,打開“測(cè)試資源管理器”:
在底部就可以看到測(cè)試資源管理器了:
在要測(cè)試的方法上面右鍵,選擇“運(yùn)行測(cè)試”就可以進(jìn)行測(cè)試了:
注意觀察測(cè)試方法前面圖標(biāo)的顏色,目前是藍(lán)色的,表示測(cè)試用例還沒有運(yùn)行過(guò):
測(cè)試用例結(jié)束以后,我們?cè)跍y(cè)試資源管理器里面可以看到結(jié)果:
綠色表示測(cè)試通過(guò)。我們還可以看到執(zhí)行測(cè)試用例消耗的時(shí)間。
如果測(cè)試結(jié)果和預(yù)期結(jié)果一致,那么測(cè)試用例前面圖標(biāo)的顏色也會(huì)變成綠色:
如果測(cè)試結(jié)果和預(yù)期結(jié)果不一致就會(huì)顯示紅色,然后需要修改代碼直到出現(xiàn)綠色圖標(biāo)。我們修改測(cè)試用例,模擬測(cè)試失敗的情況:
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.Net; using System.Net.Http; using System.Threading.Tasks; using UnitTestDemo; using Xunit; namespace TestDemo { public class ValueTests { public HttpClient _client { get; } /// <summary> /// 構(gòu)造方法 /// </summary> public ValueTests() { var server = new TestServer(WebHost.CreateDefaultBuilder() .UseStartup<Startup>()); _client = server.CreateClient(); } [Fact] public async Task GetById_ShouldBe_Ok() { // 1、Arrange var id = 1; // 2、Act // 調(diào)用異步的Get方法 var response = await _client.GetAsync($"/api/value/{id}"); //// 3、Assert //Assert.Equal(HttpStatusCode.OK, response.StatusCode); // 3、Assert // 模擬測(cè)試失敗 Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } } }
然后運(yùn)行測(cè)試用例:
2、調(diào)試單元測(cè)試
我們也可以通過(guò)添加斷點(diǎn)的方式在測(cè)試用例中進(jìn)行調(diào)試。調(diào)試單元測(cè)試很簡(jiǎn)單,只需要在要調(diào)試的方法上面右鍵選擇“調(diào)試測(cè)試”,如下圖所示:
其它操作就跟調(diào)試普通方法一樣。
除了添加斷點(diǎn)調(diào)試,我們還可以采用打印日志的方法來(lái)快速調(diào)試,xUnit可以很方便地做到這一點(diǎn)。我們修改ValueTest類:
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.Net; using System.Net.Http; using System.Threading.Tasks; using UnitTestDemo; using Xunit; using Xunit.Abstractions; namespace TestDemo { public class ValueTests { public HttpClient _client { get; } public ITestOutputHelper Output { get; } /// <summary> /// 構(gòu)造方法 /// </summary> public ValueTests(ITestOutputHelper outputHelper) { var server = new TestServer(WebHost.CreateDefaultBuilder() .UseStartup<Startup>()); _client = server.CreateClient(); Output = outputHelper; } [Fact] public async Task GetById_ShouldBe_Ok() { // 1、Arrange var id = 1; // 2、Act // 調(diào)用異步的Get方法 var response = await _client.GetAsync($"/api/value/{id}"); // 3、Assert // 模擬測(cè)試失敗 //Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); // 輸出返回信息 // Output var responseText = await response.Content.ReadAsStringAsync(); Output.WriteLine(responseText); // 3、Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } }
這里我們?cè)跇?gòu)造函數(shù)中添加了 ITestOutputHelper 參數(shù),xUnit 會(huì)將一個(gè)實(shí)現(xiàn)此接口的實(shí)例注入進(jìn)來(lái)。拿到這個(gè)實(shí)例后,我們就可以用它來(lái)輸出日志了。運(yùn)行(注意不是 Debug)此方法,運(yùn)行結(jié)束后在測(cè)試資源管理器里面查看:
點(diǎn)擊就可以看到輸出的日志了:
在上面的例子中,我們是使用的簡(jiǎn)單的Value控制器進(jìn)行測(cè)試,控制器里面沒有其他依賴關(guān)系,如果控制器里面有依賴關(guān)系該如何測(cè)試呢?方法還是一樣的,我們新建一個(gè)Student控制器,里面依賴IStudentRepository接口,代碼如下:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using UnitTest.IRepository; using UnitTest.Model; namespace UnitTestDemo.Controllers { [Route("api/student")] [ApiController] public class StudentController : ControllerBase { private readonly IStudentRepository _repository; /// <summary> /// 通過(guò)構(gòu)造函數(shù)注入 /// </summary> /// <param name="repository"></param> public StudentController(IStudentRepository repository) { _repository = repository; } /// <summary> /// get方法 /// </summary> /// <returns></returns> [HttpGet] public async Task<ActionResult<List<Student>>> Get() { return await _repository.GetList(); } } }
然后在Startup類的ConfigureServices方法中注入:
public void ConfigureServices(IServiceCollection services) { // 添加數(shù)據(jù)庫(kù)連接字符串 services.AddDbContext<AppDbContext>(options => { options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value); }); // 添加依賴注入到容器中 services.AddScoped<IStudentRepository, StudentRepository>(); services.AddControllers(); }
在單元測(cè)試項(xiàng)目中添加StudentTest類:
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using UnitTest.Model; using UnitTestDemo; using Xunit; using Xunit.Abstractions; namespace TestDemo { public class StudentTest { public HttpClient Client { get; } public ITestOutputHelper Output { get; } public StudentTest(ITestOutputHelper outputHelper) { var server = new TestServer(WebHost.CreateDefaultBuilder() .UseStartup<Startup>()); Client = server.CreateClient(); Output = outputHelper; } [Fact] public async Task Get_ShouldBe_Ok() { // 2、Act var response = await Client.GetAsync($"api/student"); // Output string context = await response.Content.ReadAsStringAsync(); Output.WriteLine(context); List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context); // Assert Assert.Equal(3, list.Count); } } }
然后運(yùn)行單元測(cè)試:
可以看到,控制器里面如果有依賴關(guān)系,也是可以使用這種方式進(jìn)行測(cè)試的。
Post方法也可以使用同樣的方式進(jìn)行測(cè)試,修改控制器,添加Post方法:
using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using UnitTest.IRepository; using UnitTest.Model; namespace UnitTestDemo.Controllers { [Route("api/student")] [ApiController] public class StudentController : ControllerBase { private readonly IStudentRepository _repository; /// <summary> /// 通過(guò)構(gòu)造函數(shù)注入 /// </summary> /// <param name="repository"></param> public StudentController(IStudentRepository repository) { _repository = repository; } /// <summary> /// get方法 /// </summary> /// <returns></returns> [HttpGet] public async Task<ActionResult<List<Student>>> Get() { return await _repository.GetList(); } /// <summary> /// Post方法 /// </summary> /// <param name="entity"></param> /// <returns></returns> [HttpPost] public async Task<bool> Post([FromBody]Student entity) { int? result = await _repository.Add(entity); if(result==null) { return false; } else { return result > 0 ? true : false; } } } }
在增加一個(gè)Post的測(cè)試方法:
using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using UnitTest.Model; using UnitTestDemo; using Xunit; using Xunit.Abstractions; namespace TestDemo { public class StudentTest { public HttpClient Client { get; } public ITestOutputHelper Output { get; } public StudentTest(ITestOutputHelper outputHelper) { var server = new TestServer(WebHost.CreateDefaultBuilder() .UseStartup<Startup>()); Client = server.CreateClient(); Output = outputHelper; } [Fact] public async Task Get_ShouldBe_Ok() { // 2、Act var response = await Client.GetAsync($"api/student"); // Output string context = await response.Content.ReadAsStringAsync(); Output.WriteLine(context); List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context); // Assert Assert.Equal(3, list.Count); } [Fact] public async Task Post_ShouldBe_Ok() { // 1、Arrange Student entity = new Student() { Name="測(cè)試9", Age=25, Gender="男" }; var str = JsonConvert.SerializeObject(entity); HttpContent content = new StringContent(str); // 2、Act content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); HttpResponseMessage response = await Client.PostAsync("api/student", content); string responseBody = await response.Content.ReadAsStringAsync(); Output.WriteLine(responseBody); // 3、Assert Assert.Equal("true", responseBody); } } }
運(yùn)行測(cè)試用例:
這樣一個(gè)簡(jiǎn)單的單元測(cè)試就完成了。
我們觀察上面的兩個(gè)測(cè)試類,發(fā)現(xiàn)這兩個(gè)類都有一個(gè)共同的特點(diǎn):都是在構(gòu)造函數(shù)里面創(chuàng)建一個(gè)HttpClient對(duì)象,我們可以把創(chuàng)建HttpClient對(duì)象抽離到一個(gè)共同的基類里面,所有的類都繼承自基類。該基類代碼如下:
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.IO; using System.Net.Http; using UnitTestDemo; namespace TestDemo { /// <summary> /// 基類 /// </summary> public class ApiControllerTestBase { /// <summary> /// 返回HttpClient對(duì)象 /// </summary> /// <returns></returns> protected HttpClient GetClient() { var builder = new WebHostBuilder() // 指定使用當(dāng)前目錄 .UseContentRoot(Directory.GetCurrentDirectory()) // 使用Startup類作為啟動(dòng)類 .UseStartup<Startup>() // 設(shè)置使用測(cè)試環(huán)境 .UseEnvironment("Testing"); var server = new TestServer(builder); // 創(chuàng)建HttpClient HttpClient client = server.CreateClient(); return client; } } }
然后修改StudentTest類,使該類繼承自上面創(chuàng)建的基類:
using Newtonsoft.Json; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using UnitTest.Model; using Xunit; using Xunit.Abstractions; namespace TestDemo { public class StudentTest: ApiControllerTestBase { public HttpClient Client { get; } public ITestOutputHelper Output { get; } public StudentTest(ITestOutputHelper outputHelper) { // var server = new TestServer(WebHost.CreateDefaultBuilder() //.UseStartup<Startup>()); // Client = server.CreateClient(); // 從父類里面獲取HttpClient對(duì)象 Client = base.GetClient(); Output = outputHelper; } [Fact] public async Task Get_ShouldBe_Ok() { // 2、Act var response = await Client.GetAsync($"api/student"); // Output string context = await response.Content.ReadAsStringAsync(); Output.WriteLine(context); List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context); // Assert Assert.Equal(3, list.Count); } [Fact] public async Task Post_ShouldBe_Ok() { // 1、Arrange Student entity = new Student() { Name="測(cè)試9", Age=25, Gender="男" }; var str = JsonConvert.SerializeObject(entity); HttpContent content = new StringContent(str); // 2、Act content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); HttpResponseMessage response = await Client.PostAsync("api/student", content); string responseBody = await response.Content.ReadAsStringAsync(); Output.WriteLine(responseBody); // 3、Assert Assert.Equal("true", responseBody); } } }
文章中的示例代碼地址:https://github.com/jxl1024/UnitTest
到此這篇關(guān)于ASP.NET Core項(xiàng)目使用xUnit進(jìn)行單元測(cè)試的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
.NET使用StackTrace獲取方法調(diào)用信息的代碼演示
StackTrace, 位于 System.Diagnostics 命名空間下,名字很直觀,它代表一個(gè)方法調(diào)用的跟蹤堆棧,里面存放著按順序排列的棧幀對(duì)象(StackFrame),每當(dāng)發(fā)生一次調(diào)用,就會(huì)壓入一個(gè)棧幀,這篇文章主要介紹了.NET使用StackTrace獲取方法調(diào)用信息,需要的朋友可以參考下2022-09-09XslTransform.Transform將結(jié)果輸出到字符串里的方法
XslTransform.Transform將結(jié)果輸出到字符串里的方法...2007-04-04asp.net動(dòng)態(tài)添加js文件調(diào)用到網(wǎng)頁(yè)的方法
這篇文章主要介紹了asp.net動(dòng)態(tài)添加js文件調(diào)用到網(wǎng)頁(yè)的方法,涉及asp.net動(dòng)態(tài)添加js的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04asp.net中關(guān)于dropdwonlist無(wú)法獲得值問題
用dropdwonlist綁定了一個(gè)數(shù)據(jù)源做選擇,但是當(dāng)提交時(shí),用控件屬性無(wú)法獲得相應(yīng)的值,打印出來(lái)每次都是顯示的第一個(gè)值2011-11-11asp.net forms身份驗(yàn)證,避免重復(fù)造輪子
最近開始一個(gè)小 asp.net 項(xiàng)目,整個(gè)項(xiàng)目需要登錄才能操作。以前大家都采用 asp 的方式 session + cookie 來(lái)實(shí)現(xiàn)身份驗(yàn)證,我一直對(duì) asp.net 自帶的 forms 驗(yàn)證早就耳聞,苦于沒實(shí)踐,今天剛好逮到機(jī)會(huì)實(shí)際應(yīng)用一下。2009-11-11