Testing gRPC

The tests in the Hello World example illustrates use of the JUnit framework. The test coverage is not complete. It only shows how to get started with testing gRPC services. You could add to it as an exercise to increase your own knowledge.

Let’s look at the test class definition in the GreeterTest.java source file:

package com.example.helloworld;

import akka.actor.ActorSystem;
import akka.grpc.GrpcClientSettings;
import akka.http.javadsl.ServerBinding;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.testkit.javadsl.TestKit;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

public class GreeterTest {

  private static ActorSystem serverSystem;
  private static ActorSystem clientSystem;
  private static GreeterServiceClient client;
  
  @BeforeClass
  public static void setup() throws Exception {
    // important to enable HTTP/2 in server ActorSystem's config
    Config config = ConfigFactory.parseString("akka.http.server.preview.enable-http2 = on")
        .withFallback(ConfigFactory.defaultApplication());
    serverSystem = ActorSystem.create("HelloWorldServer", config);
    CompletionStage<ServerBinding> bound = new GreeterServer(serverSystem).run();
    // make sure server is bound before using client
    bound.toCompletableFuture().get(5, TimeUnit.SECONDS);

    clientSystem = ActorSystem.create("HelloWorldClient");
    Materializer clientMaterializer = ActorMaterializer.create(clientSystem);
    // the host and TLS certificate config are picked up from the config file
    client = GreeterServiceClient.create(
        GrpcClientSettings.fromConfig("helloworld.GreeterService", clientSystem),
        clientMaterializer,
        clientSystem.dispatcher()
      );

  }

  @AfterClass
  public static void teardown() {
    TestKit.shutdownActorSystem(serverSystem);
    TestKit.shutdownActorSystem(clientSystem);
    serverSystem = null;
    client = null;
  }

  @Test
  public void greeterServiceRepliesToSingleRequest() throws Exception {
    HelloReply reply = client.sayHello(HelloRequest.newBuilder().setName("Alice").build())
        .toCompletableFuture()
        .get(5, TimeUnit.SECONDS);
    HelloReply expected = HelloReply.newBuilder().setMessage("Hello, Alice").build();
    assertEquals(expected, reply);
  }

}

Note how we create two ActorSystems, one for the server and another for the client. The test is then using the client to verify that it retrieves the expected responses from the server.

Unit testing

The above test example is a full integration test using real client and server, including communication via HTTP/2. For some testing of the service implementation it might be more appropriate to write unit tests without interaction via the gRPC client. Since the service interface and implementation doesn’t require any gRPC intrastructure it can be tested without binding it to a HTTP server.

package com.example.helloworld;

import akka.actor.ActorSystem;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.testkit.javadsl.TestKit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

public class GreeterServiceImplTest {

  private static ActorSystem system;
  private static Materializer materializer;
  private static GreeterService service;

  @BeforeClass
  public static void setup() {
    system = ActorSystem.create();
    materializer = ActorMaterializer.create(system);
    service = new GreeterServiceImpl(materializer);
  }

  @AfterClass
  public static void teardown() {
    TestKit.shutdownActorSystem(system);
    system = null;
    materializer = null;
  }

  @Test
  public void greeterServiceRepliesToSingleRequest() throws Exception {
    HelloReply reply = service.sayHello(HelloRequest.newBuilder().setName("Bob").build())
        .toCompletableFuture()
        .get(5, TimeUnit.SECONDS);
    HelloReply expected = HelloReply.newBuilder().setMessage("Hello, Bob").build();
    assertEquals(expected, reply);
  }

}

Add streaming tests

As an exercise to increase your understanding you could add tests for the streaming call, both as integration test and unit test style.

The Akka documentation of Testing streams might be useful.