MiniAPI的单元测试与asp.net web api的单元测试大体是相同的(毕竟都是asp.net core),只是在小细节上有一些差异,文章中会说到这点。
本文测试框架是XUnit,Mock框架是Moq,关于这两个框架和库的学习,这里就忽略了。
首先创建两个项目,API项目MiniAPI19UnitTest,UnitTest项目MiniAPI19UnitTestUT,如下:
MiniAPI19UnitTest
var builder = WebApplication.CreateBuilder(args); builder.Services.AddScoped<IOrderService, OrderService>(); var app = builder.Build(); app.MapGet("/order", (IOrderService orderService) => { return "Result:" + orderService.GetOrder("123"); }); app.MapPost("/order", (Order order, IOrderService orderService) => { return "Result:" + orderService.AddOrder(order); }); app.Run(); public interface IOrderService { bool AddOrder(Order order); string GetOrder(string orderNo); } public class OrderService : IOrderService { private readonly ILogger<OrderService> _logger; public OrderService(ILogger<OrderService> logger) { _logger = logger; } public string GetOrder(string orderNo) { return "this is my order,orderno:" + orderNo; } public bool AddOrder(Order order) { _logger.LogInformation(order.ToString()); return true; } } public record Order { public string OrderNo { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
MiniAPI19UnitTestUT:在本项目中添加引用MiniAPI19UnitTest项目
using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Moq; using System; using System.Net.Http; using System.Threading.Tasks; using Xunit; namespace MiniAPI19UnitTestUT { public class MiniAPI19Test { [Fact]//无参测试 public async Task GetOrderTest() { var orderNo = "abcd"; //用Moq来mock server接口,达到层的隔离 var mock = new Mock<IOrderService>(); mock.Setup(x => x.GetOrder(It.IsAny<string>())).Returns(orderNo); var myapp = new MyAppHostTest(services => services.AddSingleton(mock.Object)); var client = myapp.CreateClient(); var result = await client.GetStringAsync("/order"); Assert.Equal($"Result:{orderNo}", result); } [Theory]//有参测试 [InlineData(true)] [InlineData(false)] public async Task PostOrderTest(bool backResult) { var mock = new Mock<IOrderService>(); mock.Setup(x => x.AddOrder(It.IsAny<Order>())).Returns(backResult); var myapp = new MyAppHostTest(services => services.AddSingleton(mock.Object)); var client = myapp.CreateClient(); var content = new StringContent(System.Text.Json.JsonSerializer.Serialize(new Order{OrderNo = "abcd",Name = "Surface Pro 8",Price = 10000}),System.Text.Encoding.UTF8,"application/json"); var response = await client.PostAsync("/order", content); var result = await response.Content.ReadAsStringAsync(); Assert.Equal($"Result:{backResult}", result); } } //本类是加构我们MiniAPI web host的类型,封装后以供测试程序调用 class MyAppHostTest : WebApplicationFactory<Program> { private readonly Action<IServiceCollection> _services; public MyAppHostTest(Action<IServiceCollection> services) { _services = services; } protected override IHost CreateHost(IHostBuilder builder) { builder.ConfigureServices(_services); return base.CreateHost(builder); } } }
上面的代码会报错,找不到Program的,这是因为API项目是用Top Level的方式来开发的,Program的访问修饰符是internal,虽然添加引用了MiniAPI19UnitTest项目,但Program是访问不到的,这里有两个解决方案,要么不用Top Level,如下面这样写代码:
public class Program { static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); builder.Services.AddScoped<IOrderService, OrderService>(); var app = builder.Build(); app.MapGet("/test", (IOrderService orderService) => { return "Result:" + orderService.GetOrder("123"); }); app.Run(); } }
或者在MiniAPI19UnitTest.csproj文件中添加如下配置,让测试项目能访问到Program
<ItemGroup> <InternalsVisibleTo Include="MiniAPI19UnitTestUT"/> </ItemGroup>
用反射工具查看API项目结果如下,Main函数是Top-Level Entry Point方式,也看不到Program
这时,就可以开心地写自己的单元测试了。
想要更快更方便的了解相关知识,可以关注微信公众号
标签:MiniAPI,builder,UnitTest,public,services,using,var,NET6,order From: https://www.cnblogs.com/ljknlb/p/16939685.html