ASP.NET Core はインテグレーションテストが書きやすい

ASP.NET Core はインテグレーションテストをちゃんとサポートしているので、Web API のテストが書きやすい。

www.nuget.org

このパッケージを使えば、xUnit を使ったテストプロジェクトで Web API を簡単にホストしてテストできる。自分の場合、インテグレーションテスト用にベースクラスを作ることが多い。

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SampleApi.Data;
using System;
using System.Collections.Generic;

namespace SampleApi.Tests
{
    public abstract class IntegrationTestBase : IDisposable
    {
        protected TestServer Server { get; }

        protected IntegrationTestBase()
        {
            var config = new ConfigurationBuilder()
                .AddInMemoryCollection(new Dictionary<string, string>()
                {
                    ["ConnectionStrings:DefaultConnection"] = $"Server=(local);Database=SampleApi{Guid.NewGuid().ToString("N")};Integrated Security=SSPI;",
                })
                .Build();

            var host = new WebHostBuilder()
                .UseConfiguration(config)
                .UseEnvironment("Test")
                .UseStartup<Startup>();

            Server = new TestServer(host);

            GetService<ApplicationDbContext>().Database.EnsureCreated();
        }

        public void Dispose()
        {
            GetService<ApplicationDbContext>().Database.EnsureDeleted();
            Server.Dispose();
        }

        protected T GetService<T>() => Server.Host.Services.GetService<T>();
    }
}

インテグレーションテストでは実際にデータベースに接続したいので、テスト用のデータベースを毎回作り直している。当然ながらテストは遅くなるので、改善したいところ。 まぁ、インテグレーションテストはユニットテストほど高速に回す必要も今のところないかなとは思ってるんだけど。

実際に書くテストはこんな感じ。

using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace SampleApi.Tests
{
    public class AccountApiTest : IntegrationTestBase
    {
        public AccountApiTest()
            : base()
        {
        }

        [Fact]
        public async Task Loginでアクセストークンを取得できる()
        {
            using (var client = Server.CreateClient())
            {
                var response = await client.PostAsync(
                    @"/api/users/login",
                    new StringContent(
                        @"{
                            'userName': 'testuser',
                            'password': 'p@ssword'
                        }",
                        Encoding.UTF8,
                        "application/json"));
                Assert.Equal(HttpStatusCode.OK, response.StatusCode);

                var content = await response.Content.ReadAsStringAsync();
                var jObj = JObject.Parse(content);
                Assert.NotNull((string)jObj["token"]);
            }
        }
    }
}