RSS

在 gRPC JUnit 测试中优雅地清理

最佳实践是在不再需要时始终清理 gRPC 资源,例如客户端通道、服务器和以前附加的上下文。

即使对于 JUnit 测试也是如此,因为否则泄漏的资源不仅可能永远停留在您的机器上,还会干扰后续测试。一个不那么糟糕的情况是,由于先前测试中泄漏的资源,后续测试无法通过。最糟糕的情况是,如果之前通过的测试没有泄漏资源,某些后续测试就会通过,而实际上根本不应该通过。

因此,清理、清理、清理……如果任何清理不成功,则使测试失败。

一个典型的例子是

public class MyTest {
  private Server server;
  private ManagedChannel channel;
  ...
  @After
  public void tearDown() throws InterruptedException {
    // assume channel and server are not null
    channel.shutdownNow();
    server.shutdownNow();
    // fail the test if cleanup is not successful
    assert channel.awaitTermination(5, TimeUnit.SECONDS) : "channel failed to shutdown";
    assert server.awaitTermination(5, TimeUnit.SECONDS) : "server failed to shutdown";
  }
  ...
}

或者更优雅一些

public class MyTest {
  private Server server;
  private ManagedChannel channel;
  ...
  @After
  public void tearDown() throws InterruptedException {
    // assume channel and server are not null
    channel.shutdown();
    server.shutdown();
    // fail the test if cannot gracefully shutdown
    try {
      assert channel.awaitTermination(5, TimeUnit.SECONDS) : "channel cannot be gracefully shutdown";
      assert server.awaitTermination(5, TimeUnit.SECONDS) : "server cannot be gracefully shutdown";
    } finally {
      channel.shutdownNow();
      server.shutdownNow();
    }
  }
  ...
}

但是,必须将所有这些添加到每个测试中才能使其优雅地关闭,这会给您带来更多的工作,因为您需要自己编写关闭样板代码。因此,gRPC 测试库具有辅助规则,可以使这项工作不那么繁琐。

最初,引入了 JUnit 规则 GrpcServerRule,以消除关闭样板代码。此规则在测试开始时创建一个进程内服务器和通道,并在测试结束时自动关闭它们。但是,用户发现此规则过于严格,因为它不支持进程内传输以外的传输、到服务器的多个通道、自定义通道或服务器构建器选项,以及各个测试方法内的配置。

在 gRPC v1.13 版本中引入了更灵活的 JUnit 规则 GrpcCleanupRule,它也消除了关闭样板代码。但是,与 GrpcServerRule 不同,GrpcCleanupRule 根本不会自动创建任何服务器或通道。用户自己创建并启动服务器,自己创建通道,就像在普通的测试中一样。使用此规则,用户只需注册每个需要在测试结束时关闭的资源(通道或服务器),然后该规则将自动优雅地关闭它们。

您可以在运行测试方法之前注册资源

public class MyTest {
  @Rule
  public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
  ...
  private String serverName = InProcessServerBuilder.generateName();
  private Server server = grpcCleanup.register(InProcessServerBuilder
      .forName(serverName).directExecutor().addService(myServiceImpl).build().start());
  private ManagedChannel channel = grpcCleanup.register(InProcessChannelBuilder
      .forName(serverName).directExecutor().build());
  ...
}

或在每个单独的测试方法中注册资源

public class MyTest {
  @Rule
  public GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
  ...
  private String serverName = InProcessServerBuilder.generateName();
  private InProcessServerBuilder serverBuilder = InProcessServerBuilder
      .forName(serverName).directExecutor();
  private InProcessChannelBuilder channelBuilder = InProcessChannelBuilder
      .forName(serverName).directExecutor();
  ...

  @Test
  public void testFooBar() {
    ...
    grpcCleanup.register(
    	serverBuilder.addService(myServiceImpl).build().start());
    ManagedChannel channel = grpcCleanup.register(
    	channelBuilder.maxInboundMessageSize(1024).build());
    ...
  }
}

现在,有了 GrpcCleanupRule,您无需担心 JUnit 测试中 gRPC 服务器和通道的优雅关闭。因此,请试用它并在您的测试中进行清理!