Testing gRPC
The tests in the Hello World example illustrates use of the ScalaTest 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 GreeterSpec.scala
source file:
sourcepackage com.example.helloworld
import akka.actor.testkit.typed.scaladsl.ActorTestKit
import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.grpc.GrpcClientSettings
import com.typesafe.config.ConfigFactory
import org.scalatest.BeforeAndAfterAll
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scala.concurrent.duration._
class GreeterSpec
extends AnyWordSpec
with BeforeAndAfterAll
with Matchers
with ScalaFutures {
implicit val patience: PatienceConfig = PatienceConfig(scaled(5.seconds), scaled(100.millis))
// important to enable HTTP/2 in server ActorSystem's config
val conf = ConfigFactory.parseString("akka.http.server.enable-http2 = on")
.withFallback(ConfigFactory.defaultApplication())
val testKit = ActorTestKit(conf)
val serverSystem: ActorSystem[_] = testKit.system
val bound = new GreeterServer(serverSystem).run()
// make sure server is bound before using client
bound.futureValue
implicit val clientSystem: ActorSystem[_] = ActorSystem(Behaviors.empty, "GreeterClient")
val client =
GreeterServiceClient(GrpcClientSettings.fromConfig("helloworld.GreeterService"))
override def afterAll(): Unit = {
ActorTestKit.shutdown(clientSystem)
testKit.shutdownTestKit()
}
"GreeterService" should {
"reply to single request" in {
val reply = client.sayHello(HelloRequest("Alice"))
reply.futureValue should ===(HelloReply("Hello, Alice"))
}
}
}
Note how we create two ActorSystem
s, 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.
sourcepackage com.example.helloworld
import akka.actor.testkit.typed.scaladsl.ActorTestKit
import akka.actor.typed.ActorSystem
import org.scalatest.BeforeAndAfterAll
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scala.concurrent.duration._
class GreeterServiceImplSpec
extends AnyWordSpec
with BeforeAndAfterAll
with Matchers
with ScalaFutures {
val testKit = ActorTestKit()
implicit val patience: PatienceConfig = PatienceConfig(scaled(5.seconds), scaled(100.millis))
implicit val system: ActorSystem[_] = testKit.system
val service = new GreeterServiceImpl(system)
override def afterAll(): Unit = {
testKit.shutdownTestKit()
}
"GreeterServiceImpl" should {
"reply to single request" in {
val reply = service.sayHello(HelloRequest("Bob"))
reply.futureValue should ===(HelloReply("Hello, Bob"))
}
}
}
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.