Testing Actors

The tests in the Hello World example illustrates use of the ScalaTest framework. The test coverage is not complete. It simply shows how easy it is to test actor code and provides some basic concepts. You could add to it as an exercise to increase your own knowledge.

Test class definition

Let’s start by looking at the test class definition in the AkkaQuickstartSpec.scala source file:

class AkkaQuickstartSpec(_system: ActorSystem)
  extends TestKit(_system)
  with Matchers
  with FlatSpecLike
  with BeforeAndAfterAll {

The test class extends akka.test.TestKit, which is a module for integration testing of actors and actor systems. This class only uses a fraction of the functionality provided by TestKit. The other extended classes are from ScalaTest. They provide syntax and functionality for the Hello World test cases. For example, by extending FlatSpecLike, we can write test specifications in the format of “X should Y”, like this for example:

"A Greeter Actor" should "pass on a greeting message when instructed to" in {

Test methods

Now that we know some of the basic concepts, let’s look at the implemented test methods.

Integration testing can help us ensure that Actors are behaving asynchronously. This first test uses TestProbe to interrogate and verify the expected behavior. Let’s look at a source code snippet:

"A Greeter Actor" should "pass on a greeting message when instructed to" in {
  val testProbe = TestProbe()
  val helloGreetingMessage = "hello"
  val helloGreeter = system.actorOf(Greeter.props(helloGreetingMessage, testProbe.ref))
  val greetPerson = "Akka"
  helloGreeter ! WhoToGreet(greetPerson)
  helloGreeter ! Greet
  testProbe.expectMsg(500 millis, Greeting(s"$helloGreetingMessage, $greetPerson"))
}

Once we have a reference to TestProbe we pass it to Greeter as part of the constructor arguments. We thereafter send two messages to Greeter; one to set the greeting person to greet and another to trigger the sending of a Greeting. The expectMsg method on the TestProbe verifies whether the message got sent.

Full test code

And, here is the complete code:

package com.lightbend.akka.sample

import org.scalatest.{ BeforeAndAfterAll, FlatSpecLike, Matchers }
import akka.actor.{ Actor, Props, ActorSystem }
import akka.testkit.{ ImplicitSender, TestKit, TestActorRef, TestProbe }
import scala.concurrent.duration._
import Greeter._
import Printer._

class AkkaQuickstartSpec(_system: ActorSystem)
  extends TestKit(_system)
  with Matchers
  with FlatSpecLike
  with BeforeAndAfterAll {

  def this() = this(ActorSystem("AkkaQuickstartSpec"))

  override def afterAll: Unit = {
    shutdown(system)
  }

  "A Greeter Actor" should "pass on a greeting message when instructed to" in {
    val testProbe = TestProbe()
    val helloGreetingMessage = "hello"
    val helloGreeter = system.actorOf(Greeter.props(helloGreetingMessage, testProbe.ref))
    val greetPerson = "Akka"
    helloGreeter ! WhoToGreet(greetPerson)
    helloGreeter ! Greet
    testProbe.expectMsg(500 millis, Greeting(s"$helloGreetingMessage, $greetPerson"))
  }
}

The example code just scratches the surface of the functionality available in TestKit. A complete overview can be found here.

Now that we’ve reviewed all of the code. Let’s run the example again and look at its output.