Testar os serviços do gRPC no ASP.NET Core
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Por: James Newton-King
O teste é um aspecto importante da criação de software estável e de manutenção. Este artigo discute como testar os serviços gRPC do ASP.NET Core.
Há três abordagens comuns para testar os serviços gRPC:
- Teste de unidade: teste os serviços gRPC diretamente de uma biblioteca de testes de unidade.
- Teste de integração: o aplicativo gRPC é hospedado no TestServer, um servidor de teste na memória do pacote
Microsoft.AspNetCore.TestHost
. Os serviços gRPC são testados chamando-os usando um cliente gRPC de uma biblioteca de teste de unidade. - Teste manual: teste servidores gRPC com chamadas ad hoc. Para obter informações sobre como usar ferramentas de linha de comando e UI com serviços gRPC, veja Testar serviços gRPC com gRPCurl e gRPCui no ASP.NET Core.
No teste de unidade, somente o serviço gRPC está envolvido. As dependências injetadas no serviço devem ser simuladas. No teste de integração, o serviço gRPC e sua infraestrutura auxiliar fazem parte do teste. Isso inclui a inicialização de aplicativo, injeção de dependência, roteamento e autenticação e autorização.
Exemplo de serviço testável
Para demonstrar testes de serviço, examine o serviço a seguir no aplicativo de exemplo.
Exibir ou baixar código de exemplo (como baixar)
O TesterService
retorna saudações usando os quatro tipos de método do 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) });
}
}
}
O serviço gRPC anterior:
- Segue o Princípio de Dependências Explícitas.
- Espera DI (injeção de dependência) para fornecer uma instância de
IGreeter
. - Pode ser testado com um serviço
IGreeter
fictício usando uma estrutura de objeto fictício, como Moq. Um objeto fictício é um objeto fabricado com um conjunto predeterminado de comportamentos de propriedade e de método usado para teste. Para obter mais informações, confira Testes de integração no ASP.NET Core.
Serviços gRPC de teste de unidade
Uma biblioteca de teste de unidade pode testar diretamente os serviços gRPC chamando seus métodos. Os testes de unidade testam um serviço gRPC isoladamente.
[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);
}
O teste de unidade anterior:
- Simula
IGreeter
usando Moq. - Executa o método
SayHelloUnary
com uma mensagem de solicitação e umServerCallContext
. Todos os métodos de serviço têm um argumentoServerCallContext
. Neste teste, o tipo é fornecido usando o método auxiliarTestServerCallContext.Create()
. Esse método auxiliar está incluído no código de exemplo. - Faz asserções:
- Verifica se o nome da solicitação é passado para
IGreeter
. - O serviço retorna a mensagem de resposta esperada.
- Verifica se o nome da solicitação é passado para
Teste de unidade HttpContext
em métodos gRPC
Os métodos gRPC podem acessar um HttpContext da solicitação usando o método de extensão ServerCallContext.GetHttpContext
. Para testar uma unidade de um método que usa HttpContext
, o contexto deve ser definido na configuração de teste. Se HttpContext não estiver configurado, GetHttpContext
retornará null
.
Para configurar um HttpContext
durante a instalação do teste, crie uma nova instância e adicione-a à coleção ServerCallContext.UserState
usando a chave __HttpContext
.
var httpContext = new DefaultHttpContext();
var serverCallContext = TestServerCallContext.Create();
serverCallContext.UserState["__HttpContext"] = httpContext;
Execute os métodos de serviço com esse contexto de chamada para usar a instância HttpContext
configurada.
Serviços gRPC de teste de integração
Os testes de integração avaliam os componentes de um aplicativo em um nível mais amplo do que os testes de unidade. O aplicativo gRPC é hospedado no TestServer, um servidor de teste na memória do pacote Microsoft.AspNetCore.TestHost
.
Uma biblioteca de teste de unidade inicia o aplicativo gRPC e, em seguida, os serviços gRPC são testados usando o cliente gRPC.
O código de exemplo contém a infraestrutura para possibilitar o teste de integração:
- A classe
GrpcTestFixture<TStartup>
configura o host ASP.NET Core e inicia o aplicativo gRPC em um servidor de teste na memória. - A classe
IntegrationTestBase
é o tipo base do qual os testes de integração herdam. Ela contém o estado do acessório e as APIs para criar um cliente gRPC para chamar o aplicativo gRPC.
[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);
}
O teste de integração anterior:
- Cria um cliente gRPC usando o canal fornecido por
IntegrationTestBase
. Esse tipo está incluído no código de exemplo. - Chama o método
SayHelloUnary
usando o cliente gRPC. - Declara o serviço que retorna a mensagem de resposta esperada.
Injetar dependências fictícias
Use ConfigureWebHost
no acessório para substituir dependências. A substituição de dependências é útil quando uma dependência externa não está disponível no ambiente de teste. Por exemplo, um aplicativo que usa um gateway de pagamento externo não deve chamar a dependência externa ao executar testes. Em vez disso, use um gateway fictício para o teste.
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) =>
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("Name not provided.");
}
return $"Test {s}";
});
Fixture.ConfigureWebHost(builder =>
{
builder.ConfigureServices(
services => services.AddSingleton(mockGreeter.Object));
});
}
[Fact]
public async Task SayHelloUnaryTest_MockGreeter_Success()
{
// Arrange
var client = new Tester.TesterClient(Channel);
// Act
var response = await client.SayHelloUnaryAsync(
new HelloRequest { Name = "Joe" });
// Assert
Assert.Equal("Test Joe", response.Message);
}
O teste de integração anterior:
- No construtor (
MockedGreeterServiceTests
) da classe de teste:- Simula
IGreeter
usando Moq. - Substitui o
IGreeter
registrado com injeção de dependência usandoConfigureWebHost
.
- Simula
- Chama o método
SayHelloUnary
usando o cliente gRPC. - Declara a mensagem de resposta esperada com base na instância fictícia
IGreeter
.