ASP.NET Core에서 gRPC 서비스 테스트

작성자: James Newton-King

테스트는 안정적이고 유지 관리 가능한 소프트웨어를 구축하는 데 있어 중요한 측면입니다. 이 문서에서는 ASP.NET Core gRPC 서비스를 테스트하는 방법을 설명합니다.

gRPC 서비스를 테스트하는 일반적인 방법에는 세 가지가 있습니다.

  • 단위 테스트: 단위 테스트 라이브러리에서 직접 gRPC 서비스를 테스트합니다.
  • 통합 테스트: gRPC 앱은 Microsoft.AspNetCore.TestHost 패키지의 메모리 내 테스트 서버인 TestServer에서 호스트됩니다. 단위 테스트 라이브러리의 gRPC 클라이언트를 통해 gRPC 서비스를 호출하여 테스트합니다.
  • 수동 테스트: 임시 호출을 사용하여 gRPC 서버를 테스트합니다. gRPC 서비스에서 명령줄 및 UI 도구를 사용하는 방법에 대한 자세한 내용은 ASP.NET Core에서 gRPCurl를 사용하여 테스트 gRPC 서비스 테스트를 참조하세요.

단위 테스트에는 gRPC 서비스만 포함됩니다. 서비스에 주입되는 종속성은 모의되어야 합니다. 통합 테스트에서 gRPC 서비스 및 해당 보조 인프라는 테스트의 일부입니다. 여기에는 앱 시작, 종속성 주입, 라우팅 및 인증, 권한 부여가 포함됩니다.

테스트 가능한 서비스 예제

서비스 테스트를 시연하려면 샘플 앱에서 다음 서비스를 검토합니다.

예제 코드 살펴보기 및 다운로드 (다운로드 방법)

TesterService는 gRPC의 네 가지 메서드 형식을 사용하여 인사말을 반환합니다.

public class TesterService : Tester.TesterBase
{
    private readonly IGreeter _greeter;

    public TesterService(IGreeter greeter)
    {
        _greeter = greeter;
    }

    public override Task<HelloReply> SayHelloUnary(HelloRequest request,
        ServerCallContext context)
    {
        var message = _greeter.Greet(request.Name);
        return Task.FromResult(new HelloReply { Message = message });
    }

    public override async Task SayHelloServerStreaming(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        var i = 0;
        while (!context.CancellationToken.IsCancellationRequested)
        {
            var message = _greeter.Greet($"{request.Name} {++i}");
            await responseStream.WriteAsync(new HelloReply { Message = message });

            await Task.Delay(1000);
        }
    }

    public override async Task<HelloReply> SayHelloClientStreaming(
        IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context)
    {
        var names = new List<string>();

        await foreach (var request in requestStream.ReadAllAsync())
        {
            names.Add(request.Name);
        }

        var message = _greeter.Greet(string.Join(", ", names));
        return new HelloReply { Message = message };
    }

    public override async Task SayHelloBidirectionalStreaming(
        IAsyncStreamReader<HelloRequest> requestStream,
        IServerStreamWriter<HelloReply> responseStream,
        ServerCallContext context)
    {
        await foreach (var request in requestStream.ReadAllAsync())
        {
            await responseStream.WriteAsync(
                new HelloReply { Message = _greeter.Greet(request.Name) });
        }
    }
}

이전 gRPC 서비스:

  • 명시적 종속성 원칙을 따릅니다.
  • DI(종속성 주입)에서 IGreeter의 인스턴스를 제공할 것으로 기대합니다.
  • Moq와 같은 모의 개체 프레임워크를 사용하여 모의 IGreeter 서비스로 테스트할 수 있습니다. ‘모의 개체’는 테스트에 사용되는 사전 결정된 속성 및 메서드 동작 집합이 있는 제작된 개체입니다. 자세한 내용은 ASP.NET Core의 통합 테스트를 참조하세요.

gRPC 서비스 단위 테스트

단위 테스트 라이브러리는 해당 메서드를 호출하여 gRPC 서비스를 직접 테스트할 수 있습니다. 단위 테스트는 격리 상태에서 gRPC 서비스를 테스트합니다.

[Fact]
public async Task SayHelloUnaryTest()
{
    // Arrange
    var mockGreeter = new Mock<IGreeter>();
    mockGreeter.Setup(
        m => m.Greet(It.IsAny<string>())).Returns((string s) => $"Hello {s}");
    var service = new TesterService(mockGreeter.Object);

    // Act
    var response = await service.SayHelloUnary(
        new HelloRequest { Name = "Joe" }, TestServerCallContext.Create());

    // Assert
    mockGreeter.Verify(v => v.Greet("Joe"));
    Assert.Equal("Hello Joe", response.Message);
}

위의 단위 테스트:

  • Moq를 사용하여 IGreeter를 모의합니다.
  • 요청 메시지 및 ServerCallContext를 사용하여 SayHelloUnary를 실행합니다. 모든 서비스 메서드에는 ServerCallContext 인수가 있습니다. 이 테스트에서는 TestServerCallContext.Create() 도우미 메서드를 사용하여 형식을 제공합니다. 이 도우미 메서드는 샘플 코드에 포함되어 있습니다.
  • 다음을 어설션합니다.
    • 요청 이름이 IGreeter에 전달되었는지 확인합니다.
    • 서비스가 예상된 응답 메시지를 반환합니다.

gRPC 메서드에서 HttpContext 단위 테스트

gRPC 메서드는 ServerCallContext.GetHttpContext 확장 메서드를 사용하여 요청의 HttpContext에 액세스할 수 있습니다. HttpContext를 사용하는 메서드를 단위 테스트하려면 테스트 설정에서 컨텍스트를 구성해야 합니다. HttpContext가 구성되지 않은 경우 GetHttpContext에서 null을 반환합니다.

테스트를 설정하는 동안 HttpContext를 구성하려면 새 인스턴스를 만들고 __HttpContext 키를 사용하여 ServerCallContext.UserState 컬렉션에 추가합니다.

var httpContext = new DefaultHttpContext();

var serverCallContext = TestServerCallContext.Create();
serviceCallContext.UserState["__HttpContext"] = httpContext;

이 호출 컨텍스트로 서비스 메서드를 실행하여 구성된 HttpContext 인스턴스를 사용합니다.

gRPC 서비스 통합 테스트

통합 테스트는 단위 테스트보다 광범위한 수준에서 앱의 구성 요소를 평가합니다. GRPC 앱은 Microsoft.AspNetCore.TestHost 패키지의 메모리 내 테스트 서버인 TestServer에서 호스트됩니다.

단위 테스트 라이브러리는 gRPC 앱을 시작한 다음 gRPC 클라이언트를 사용하여 gRPC 서비스를 테스트합니다.

예제 코드에는 통합 테스트가 가능한 인프라가 포함되어 있습니다.

  • GrpcTestFixture<TStartup> 클래스는 ASP.NET Core 호스트를 구성하고 메모리 내 테스트 서버에서 gRPC 앱을 시작합니다.
  • IntegrationTestBase 클래스는 통합 테스트가 상속하는 기본 형식입니다. gRPC 앱을 호출하는 gRPC 클라이언트를 만들기 위한 픽스쳐 상태 및 API가 포함되어 있습니다.
[Fact]
public async Task SayHelloUnaryTest()
{
    // Arrange
    var client = new Tester.TesterClient(Channel);

    // Act
    var response = await client.SayHelloUnaryAsync(new HelloRequest { Name = "Joe" });

    // Assert
    Assert.Equal("Hello Joe", response.Message);
}

이전 통합 테스트:

  • IntegrationTestBase에서 제공하는 채널을 사용하여 gRPC 클라이언트를 만듭니다. 이 형식은 샘플 코드에 포함되어 있습니다.
  • gRPC 클라이언트를 사용하여 SayHelloUnary 메서드를 호출합니다.
  • 서비스가 예상된 응답 메시지를 반환함을 어설션합니다.

모의 종속성 주입

픽스쳐에서 ConfigureWebHost를 사용하여 종속성을 재정의합니다. 종속성 재정의는 테스트 환경에서 외부 종속성을 사용할 수 없는 경우에 유용합니다. 예를 들어 외부 지불 게이트웨이를 사용하는 앱은 테스트를 실행할 때 외부 종속성을 호출하지 않아야 합니다. 대신 모의 게이트웨이를 테스트에 사용합니다.

public MockedGreeterServiceTests(GrpcTestFixture<Startup> fixture,
    ITestOutputHelper outputHelper) : base(fixture, outputHelper)
{
    var mockGreeter = new Mock<IGreeter>();
    mockGreeter.Setup(
        m => m.Greet(It.IsAny<string>())).Returns((string s) => $"Test {s}");

    Fixture.ConfigureWebHost(builder =>
    {
        builder.ConfigureServices(
            services => services.AddSingleton(mockGreeter.Object));
    });
}

[Fact]
public async Task SayHelloUnaryTest_MockGreeter()
{
    // Arrange
    var client = new Tester.TesterClient(Channel);

    // Act
    var response = await client.SayHelloUnaryAsync(
        new HelloRequest { Name = "Joe" });

    // Assert
    Assert.Equal("Test Joe", response.Message);
}

이전 통합 테스트:

  • 테스트 클래스의 (MockedGreeterServiceTests) 생성자에서 다음을 수행합니다.
    • Moq를 사용하여 IGreeter를 모의합니다.
    • ConfigureWebHost를 사용하여 종속성 주입에 등록된 IGreeter를 재정의합니다.
  • gRPC 클라이언트를 사용하여 SayHelloUnary 메서드를 호출합니다.
  • 모의 IGreeter 인스턴스를 기반으로 필요한 응답 메시지를 어설션합니다.

추가 리소스