0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MiniAPI(二十九):UnitTest

Posted at

MiniAPIのユニットテストはasp.net web apiのユニットテストと大体同じです(どちらもasp.net coreですから)。しかし、細かな違いがいくつかあり、この記事で詳しく説明します。ここで使っているテストフレームワークはXUnit、モックフレームワークは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を使って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ホストのクラスをカプセル化し、テストプログラムで使用する
    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プロジェクトがトップレベルの方式で開発されており、Programのアクセス修飾子がinternalであるためです。MiniAPI19UnitTestプロジェクトへの参照を追加しても、Programにはアクセスできません。解決策は二つあります。トップレベルを使用しない方法は以下のようになります:

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関数がトップレベルエントリポイント方式であり、Programが見えなくなっています。これで、自分のユニットテストを楽しく書くことができます。

(Translated by GPT)

元のリンク:https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247485040&idx=1&sn=c3d78501a15dce72b97080a37e8e666a&chksm=9f00595aa877d04cbce97e68a5f85c7de2f56817f32ee318e2e204bfc6c16b6b00fa1ec13ee2&token=732480897&lang=zh_CN#rd

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?