최소 API 앱의 단위 및 통합 테스트

Fiyaz Bin Hasan, Rick Anderson

통합 테스트 소개

통합 테스트는 단위 테스트보다 광범위한 수준에서 앱의 구성 요소를 평가합니다. 단위 테스트는 개별 클래스 메서드와 같은 격리된 소프트웨어 구성 요소를 테스트하는 데 사용됩니다. 통합 테스트는 두 개 이상의 앱 구성 요소가 함께 작동하여 요청을 완전히 처리하는 데 필요한 모든 구성 요소를 포함하여 예상되는 결과를 생성하는지 확인합니다.

이러한 광범위한 테스트는 다음과 같은 구성 요소를 포함하여 앱의 인프라 및 전체 프레임워크를 테스트하는 데 사용됩니다.

  • Database
  • 파일 시스템
  • 네트워크 어플라이언스
  • 요청-응답 파이프라인

단위 테스트는 인프라 구성 요소 대신 가짜 또는 모의 개체로 알려져 있는 제작된 구성 요소를 사용합니다.

단위 테스트와 달리, 통합 테스트는 다음과 같습니다.

  • 앱이 프로덕션 환경에서 사용하는 실제 구성 요소를 사용합니다.
  • 더 많은 코드와 데이터 처리가 필요합니다.
  • 실행하는 데 시간이 더 오래 걸립니다.

따라서 통합 테스트 사용은 가장 중요한 인프라 시나리오로 제한합니다. 단위 테스트 또는 통합 테스트를 사용하여 동작을 테스트할 수 있는 경우 단위 테스트를 선택합니다.

통합 테스트를 논의할 때 테스트된 프로젝트를 주로 테스트 중인 시스템 또는 간단히 "SUT"라고 합니다. 이 문서 전체에서 테스트 중인 ASP.NET Core 앱을 참조하기 위해 사용합니다.

데이터베이스 및 파일 시스템을 사용하는 데이터 및 파일 액세스의 모든 데이터 순열에 대해 통합 테스트를 작성하지 마세요. 앱에서 데이터베이스 및 파일 시스템과 상호 작용하는 위치 수에 관계없이, 주요 읽기, 쓰기, 업데이트 및 삭제 통합 테스트 세트를 통해 일반적으로 데이터베이스 및 파일 시스템 구성 요소를 적절하게 테스트할 수 있습니다. 이러한 구성 요소와 상호 작용하는 메서드 논리의 루틴 테스트를 위해 단위 테스트를 사용합니다. 단위 테스트에서 인프라 가짜 또는 모의 시험을 사용하면 테스트 실행 속도가 더 빨라집니다.

ASP.NET Core의 통합 테스트

ASP.NET Core의 통합 테스트에는 다음이 필요합니다.

  • 테스트 프로젝트는 테스트를 포함하고 실행하는 데 사용됩니다. 테스트 프로젝트에는 SUT에 대한 참조가 있습니다.
  • 테스트 프로젝트는 SUT에 대한 테스트 웹 호스트를 만들고 테스트 서버 클라이언트를 사용하여 SUT에서 요청 및 응답을 처리합니다.
  • Test Runner는 테스트를 실행하고 테스트 결과를 보고하는 데 사용됩니다.

통합 테스트는 일반적인 정렬, 실행어설션 테스트 단계를 포함하는 이벤트 시퀀스를 따릅니다.

  1. SUT의 웹 호스트가 구성되어 있습니다.
  2. 앱에 요청을 제출하는 테스트 서버 클라이언트가 만들어집니다.
  3. 테스트 정렬 단계가 실행됩니다. 테스트 앱이 요청을 준비합니다.
  4. Act 테스트 단계가 실행됩니다. 클라이언트가 요청을 제출하고 응답을 받습니다.
  5. Assert 테스트 단계가 실행됩니다. 실제 응답은 예상 응답에 따라 통과 또는 실패유효성을 검사합니다.
  6. 모든 테스트가 실행될 때까지 프로세스가 계속됩니다.
  7. 테스트 결과가 보고됩니다.

일반적으로 테스트 웹 호스트는 테스트 실행을 위해 앱의 일반 웹 호스트와는 다르게 구성됩니다. 예를 들어, 다른 데이터베이스 또는 다른 앱 설정을 테스트에 사용할 수 있습니다.

테스트 웹 호스트와 메모리 내 테스트 서버(TestServer)와 같은 인프라 구성 요소는 Microsoft.AspNetCore.Mvc.Testing 패키지에서 제공되거나 관리됩니다. 이 패키지를 사용하면 테스트 생성 및 실행이 간소화됩니다.

Microsoft.AspNetCore.Mvc.Testing 패키지는 다음 작업을 처리합니다.

  • SUT의 종속성 파일(.deps)을 테스트 프로젝트의 bin 디렉터리에 복사합니다.
  • 테스트를 실행하면 고정 파일 및 페이지/뷰를 찾을 수 있도록 콘텐츠 루트를 SUT의 프로젝트 루트로 설정합니다.
  • TestServer에서 SUT 부트스트랩을 간소화하기 위해 WebApplicationFactory 클래스를 제공합니다.

단위 테스트 설명서에서는 테스트를 실행하는 방법에 대한 자세한 지침과 테스트 및 테스트 클래스의 이름을 설정하는 방법에 대한 권장 사항을 제공하고 테스트 프로젝트 및 Test Runner를 설정하는 방법을 설명합니다.

통합 테스트에서 다른 프로젝트로 단위 테스트를 분리합니다. 테스트를 분리하는 중:

  • 인프라 테스트 구성 요소가 실수로 단위 테스트에 포함되지 않도록 할 수 있습니다.
  • 실행되는 테스트 집합을 제어할 수 있습니다.

GitHub의 샘플 코드는 최소 API 앱에서 단위 및 통합 테스트의 예를 제공합니다.

IResult 구현 형식

Microsoft.AspNetCore.Http.HttpResults 네임스페이스의 퍼블릭 IResult 구현 형식은 람다 대신 명명된 메서드를 사용할 때 최소 경로 처리기를 단위 테스트하는 데 사용할 수 있습니다.

다음 코드에서는 NotFound<TValue> 값을 사용합니다.

[Fact]
public async Task GetTodoReturnsNotFoundIfNotExists()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var notFoundResult = (NotFound) result.Result;

    Assert.NotNull(notFoundResult);
}

다음 코드에서는 Ok<TValue> 값을 사용합니다.

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title",
        Description = "Test description",
        IsDone = false
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var okResult = (Ok<Todo>)result.Result;

    Assert.NotNull(okResult.Value);
    Assert.Equal(1, okResult.Value.Id);
}

추가 리소스