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를 재정의합니다.
- Moq를 사용하여
- gRPC 클라이언트를 사용하여
SayHelloUnary메서드를 호출합니다. - 모의
IGreeter인스턴스를 기반으로 필요한 응답 메시지를 어설션합니다.