在测试中模拟 gRPC 客户端

作者:James Newton-King

测试是构建稳定且可维护的软件的一个重要方面。 编写高质量测试的一部分是删除外部依赖项。 本文讨论如何在测试中使用模拟 gRPC 客户端来删除对外部服务器的 gRPC 调用。

可测试客户端应用示例

若要演示客户端应用测试,请在示例应用中查看以下类型。

查看或下载示例代码如何下载

Worker 是调用 gRPC 服务器的 BackgroundService

public class Worker : BackgroundService
{
    private readonly Tester.TesterClient _client;
    private readonly IGreetRepository _greetRepository;

    public Worker(Tester.TesterClient client, IGreetRepository greetRepository)
    {
        _client = client;
        _greetRepository = greetRepository;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var count = 0;
        while (!stoppingToken.IsCancellationRequested)
        {
            count++;

            var reply = await _client.SayHelloUnaryAsync(
                new HelloRequest { Name = $"Worker {count}" });

            _greetRepository.SaveGreeting(reply.Message);

            await Task.Delay(1000, stoppingToken);
        }
    }
}

前面的类型:

  • 遵循显式依赖关系原则
  • TesterClient 是在生成过程中基于 test.proto 文件由工具包 Grpc.Tools 自动生成的。
  • 期望依赖关系注入 (DI) 提供 TesterClientIGreetRepository 的实例。 应用配置为使用 gRPC 客户端工厂创建 TesterClient
  • 可以使用 mock 对象框架(如 Moq)对模拟 IGreetRepository 服务和 TesterClient 客户端进行测试。 模拟对象是由一组预先确定的用于测试的属性和方法行为的对象。 有关详细信息,请参阅 ASP.NET Core 中的集成测试

有关 Grpc.Tools 自动生成的 C# 资产的详细信息,请参阅使用 C# 的 gRPC 服务:生成的 C# 资产

模拟 gRPC 客户端

gRPC 客户端是.proto 文件生成的具体客户端类型。 具体 gRPC 客户端具有转换为 .proto 文件中 gRPC 服务的方法。 例如,名为 Greeter 的服务生成 GreeterClient 类型(包含调用服务的方法)。

模拟框架可以模拟 gRPC 客户端类型。 当模拟客户端传递给该类型时,测试将使用模拟方法,而不是将 gRPC 调用发送给服务器。

[Fact]
public async Task Greeting_Success_RepositoryCalled()
{
    // Arrange
    var mockRepository = new Mock<IGreetRepository>();

    var mockCall = CallHelpers.CreateAsyncUnaryCall(new HelloReply { Message = "Test" });
    var mockClient = new Mock<Tester.TesterClient>();
    mockClient
        .Setup(m => m.SayHelloUnaryAsync(
            It.IsAny<HelloRequest>(), null, null, CancellationToken.None))
        .Returns(mockCall);

    var worker = new Worker(mockClient.Object, mockRepository.Object);

    // Act
    await worker.StartAsync(CancellationToken.None);

    // Assert
    mockRepository.Verify(v => v.SaveGreeting("Test"));
}

前面的单元测试:

  • 使用 Moq 模拟 IGreetRepositoryTesterClient
  • 启动辅助角色。
  • 确认 SaveGreeting 是使用模拟的 TesterClient 返回的问候消息调用的。

其他资源