출시·고도화 중
ASP.NET Core 안내서 · 5/6
ASP.NET Core 앱은 보통 두 단계로 테스트합니다. 서비스와 로직을 검사하는 단위 테스트, 그리고 WebApplicationFactory로 앱 전체를 메모리에 띄워 검사하는 통합 테스트입니다. .NET에서는 xUnit이 가장 널리 쓰이며, NUnit과 MSTest도 같은 방식으로 동작합니다.
dotnet new xunit -o tests/TodoApi.Tests
dotnet add tests/TodoApi.Tests/TodoApi.Tests.csproj reference src/TodoApi/TodoApi.csproj
dotnet add tests/TodoApi.Tests/TodoApi.Tests.csproj package Microsoft.AspNetCore.Mvc.Testing
dotnet sln add tests/TodoApi.Tests/TodoApi.Tests.csproj
dotnet testdotnet test는 솔루션의 모든 테스트 프로젝트를 빌드하고 실행합니다. 편집기의 테스트 탐색기에서도 같은 테스트가 보입니다.
일반 클래스는 웹 호스트 없이 바로 테스트할 수 있습니다.
public static class Slug
{
public static string Create(string title) =>
string.Join('-', title.ToLowerInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries));
}public class SlugTests
{
[Fact]
public void Joins_words_with_hyphens()
{
Assert.Equal("hello-asp-net", Slug.Create("Hello ASP NET"));
}
[Theory]
[InlineData(" spaced out ", "spaced-out")]
[InlineData("One", "one")]
public void Normalizes_input(string input, string expected)
{
Assert.Equal(expected, Slug.Create(input));
}
}[Fact]는 테스트 하나를, [Theory]는 [InlineData] 줄마다 같은 테스트를 반복 실행합니다. 의존성이 있는 서비스는 인터페이스나 TimeProvider 같은 추상화에 의존하게 만들어, 테스트에서 직접 만든 가짜 객체나 NSubstitute, Moq 같은 라이브러리의 대역을 넘길 수 있게 합니다.
Microsoft.AspNetCore.Mvc.Testing 패키지의 WebApplicationFactory<TEntryPoint>는 앱을 메모리 안의 테스트 서버로 띄우고 HttpClient를 건네줍니다. 실제 Program.cs, 설정, DI를 그대로 쓰므로 라우팅, 미들웨어, 직렬화까지 함께 검사됩니다.
최상위 문을 쓰면 컴파일러가 만드는 Program 클래스가 public이 아닙니다. 최신 SDK는 아래 선언을 자동으로 만들어 주기도 하지만, Program.cs 끝에 직접 적어 두면 어느 버전에서나 동작합니다.
public partial class Program { }using System.Net;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
public class TodoApiTests(WebApplicationFactory<Program> factory)
: IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task Get_unknown_todo_returns_404()
{
var client = factory.CreateClient();
var response = await client.GetAsync("/todos/999");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task Post_then_get_returns_the_todo()
{
var client = factory.CreateClient();
var created = await client.PostAsJsonAsync("/todos", new { title = "Write tests" });
created.EnsureSuccessStatusCode();
var todo = await created.Content.ReadFromJsonAsync<Todo>();
var fetched = await client.GetFromJsonAsync<Todo>($"/todos/{todo!.Id}");
Assert.Equal("Write tests", fetched!.Title);
}
}IClassFixture는 테스트 클래스마다 앱을 하나 만들어 그 클래스의 테스트끼리 공유하므로, 테스트가 서로의 데이터에 기대지 않도록 작성합니다.
WebApplicationFactory를 상속하면 환경을 정하거나 서비스를 바꿔 끼울 수 있습니다. ConfigureTestServices는 앱 자체의 서비스 등록이 끝난 뒤에 실행됩니다.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Time.Testing;
public class TodoApiFactory : WebApplicationFactory<Program>
{
public FakeTimeProvider Time { get; } = new();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
builder.ConfigureTestServices(services =>
{
services.RemoveAll<TimeProvider>();
services.AddSingleton<TimeProvider>(Time);
});
}
}FakeTimeProvider는 Microsoft.Extensions.TimeProvider.Testing 패키지에 있으며, 테스트에서 Time.Advance(...)로 시계를 앞당길 수 있습니다. 데이터베이스는 Testing 환경이 버려도 되는 별도 데이터베이스를 가리키게 하거나, Testcontainers로 Docker에서 실제 데이터베이스를 띄워 씁니다.
dotnet test --filter "FullyQualifiedName~TodoApiTests"
dotnet test --collect:"XPlat Code Coverage"dotnet new xunit으로 xUnit 프로젝트를 만들고 dotnet test로 실행합니다.WebApplicationFactory<Program>는 통합 테스트를 위해 실제 앱을 메모리에 띄웁니다.ConfigureTestServices로 환경과 서비스를 바꿉니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.