在 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 服务器和通道的优雅关闭问题。所以,快来尝试一下,并在您的测试中进行清理吧!