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.testkit.typed.javadsl.ActorTestKit;
import akka.actor.testkit.typed.javadsl.TestKitJunitResource;
import akka.actor.typed.ActorSystem;
import akka.actor.typed.javadsl.Behaviors;
import akka.grpc.GrpcClientSettings;
import akka.http.javadsl.ServerBinding;

import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

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

import static org.junit.Assert.assertEquals;

public class GreeterTest {

  // important to enable HTTP/2 in server ActorSystem's config
  private static final Config config = ConfigFactory
          .parseString("akka.http.server.preview.enable-http2 = on")
          .withFallback(ConfigFactory.defaultApplication());

  @ClassRule
  public static final TestKitJunitResource testKit = new TestKitJunitResource(config);

  private static ActorSystem<?> serverSystem = testKit.system();
  private static ActorSystem<?> clientSystem;
  private static GreeterServiceClient client;
  
  @BeforeClass
  public static void setup() throws Exception {
    CompletionStage<ServerBinding> bound = new GreeterServer(serverSystem).run();
    // make sure server is bound before using client
    bound.toCompletableFuture().get(5, TimeUnit.SECONDS);

    clientSystem = ActorSystem.create(Behaviors.empty(), "GreeterClient");
    // the host and TLS certificate config are picked up from the config file
    client = GreeterServiceClient.create(
        GrpcClientSettings.fromConfig("helloworld.GreeterService", clientSystem),
        clientSystem
      );
  }

  @AfterClass
  public static void teardown() {
    ActorTestKit.shutdown(clientSystem);
    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 infrastructure it can be tested without binding it to a HTTP server.

package com.example.helloworld;

import akka.actor.testkit.typed.javadsl.ActorTestKit;
import akka.actor.testkit.typed.javadsl.TestKitJunitResource;
import akka.actor.typed.ActorSystem;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

public class GreeterServiceImplTest {

  @ClassRule
  public static final TestKitJunitResource testKit = new TestKitJunitResource();

  private static ActorSystem<?> system = testKit.system();
  private static GreeterService service;

  @BeforeClass
  public static void setup() {
    service = new GreeterServiceImpl(system);
  }

  @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.