Fork me on GitHub

.NET6之MiniAPI(二十九):UnitTest

  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

   这时,就可以开心地写自己的单元测试了。

  想要更快更方便的了解相关知识,可以关注微信公众号 

 

 

posted @ 2022-11-30 20:43  桂素伟  阅读(100)  评论(0编辑  收藏  举报