测试是软件开发中重要的一步,本文介绍如何创建grpc的测试来保证代码的质量。这里都沿用的server和client代码
服务端测试
public class HelloWorldServerTest { //定义服务名字用于绑定客户端调用的服务端 private static final String UNIQUE_SERVER_NAME = "in-process server for " + HelloWorldServerTest.class; //创建一个进程内的server private final Server inProcessServer = InProcessServerBuilder.forName(UNIQUE_SERVER_NAME).addService(new HelloServiceImpl()).directExecutor().build(); //创建一个进程内的channel private final ManagedChannel inProcessChannel = InProcessChannelBuilder.forName(UNIQUE_SERVER_NAME).directExecutor().build(); /** * 初始化进城内服务器 */ @Before public void setUp() throws Exception { inProcessServer.start(); } /** * 使用stub调用server并验证返回结果 */ @Test public void testSimple() throws Exception { HelloServiceGrpc.HelloServiceBlockingStub blockingStub = HelloServiceGrpc.newBlockingStub(inProcessChannel); String testName = "World"; //调用服务 blog.proto.ProtoObj.Result reply = blockingStub.simpleHello(ProtoObj.Person.newBuilder().setMyName(testName).build()); //验证· assertEquals("hello, " + testName, reply.getString()); } /** * 关闭服务器和channel */ @After public void tearDown() { inProcessChannel.shutdownNow(); inProcessServer.shutdownNow(); }}
客户端测试
在客户端测试之前,需要对客户端进行一些改动,需要将channel变为实例变量,方便注入,这里以simple服务为例:
public class HelloClient { private static final Logger logger = Logger.getLogger(HelloClient.class.getName()); //提出的channel private final ManagedChannel channel; //stub private final HelloServiceGrpc.HelloServiceBlockingStub blockingStub; //在构造方法中传入channel public HelloClient(ManagedChannel channel){ this.channel=channel; blockingStub = HelloServiceGrpc.newBlockingStub(channel); } public void simple(String name) { logger.info("Will try to greet " + name + " ..."); ProtoObj.Person person = ProtoObj.Person.newBuilder().setMyName(name).build(); try { ProtoObj.Result response = blockingStub.simpleHello(person); logger.info(response.getString()); } catch (StatusRuntimeException e) { logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); return; } }}
之后进行测试:
public class HelloWorldClientTest { //spy一个服务实例 private final HelloServiceGrpc.HelloServiceImplBase serviceImpl = spy(new HelloServiceGrpc.HelloServiceImplBase() { }); private Server fakeServer; private HelloClient client; ManagedChannel channel; /** * 模拟server和channel并创建client */ @Before public void setUp() throws Exception { String uniqueServerName = "fake server.java for " + getClass(); //创建进程内服务和channel fakeServer = InProcessServerBuilder.forName(uniqueServerName).directExecutor().addService(serviceImpl).build().start(); InProcessChannelBuilder channelBuilder = InProcessChannelBuilder.forName(uniqueServerName).directExecutor(); channel=channelBuilder.build(); //创建client client = new HelloClient(channel); } @Test public void greet_messageDeliveredToServer() { ArgumentCaptor< ProtoObj.Person> requestCaptor = ArgumentCaptor.forClass( ProtoObj.Person.class); String testName = "world"; client.simple(testName); //验证服务器期望收到的参数 verify(serviceImpl).simpleHello(requestCaptor.capture(), Matchers.>any()); assertEquals(testName, requestCaptor.getValue().getMyName()); } @After public void tearDown() throws Exception { //关闭channel和server channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); fakeServer.shutdownNow(); }}
总结
在单元测试时一大麻烦的是多线程的处理,使用InProcessServerBuilder会创建进程内的调用,server和client将同步在main线程中调用,这样就减少了多线程、异步测试会遇到的问题。