Tuesday, January 14, 2014

Akka: Testing that an actor sends a message back to the sender

A common pattern when using actors is for actor A to send a message to Actor B and then for Actor B to send a message back.

This can be tested using the Akka testkit along with Scala Test.

Take the following very simple example. We have an Actor called Server which should accept Startup messages and respond with a Ready message when it is ready for clients to send additional messages.

We can start with a noddy implementation that does nothing:

package batey.akka.testing.backtosender
import akka.actor.Actor
class Server extends Actor {
def receive: Actor.Receive = {
case msg @ _ => {
println("I should really send something back!")
}
}
}
case object Messages {
case object Startup
case object Ready
}
view raw Server.scala hosted with ❤ by GitHub
Then write a test to specify the behaviour. Here I've used TestKit along with Scala Test FunSuite.

package batey.akka.testing.backtosender
import akka.testkit.{ImplicitSender, TestKit}
import akka.actor.{Props, ActorSystem}
import org.scalatest.FunSuiteLike
class ServerTest extends TestKit(ActorSystem("TestSystem")) with FunSuiteLike with ImplicitSender {
test("Should send back Ready message when Startup message is received") {
val actorRef = system.actorOf(Props[Server])
actorRef ! Messages.Startup
expectMsg(Messages.Ready)
}
}
This test will fail with the following error message:

assertion failed: timeout (3 seconds) during expectMsg while waiting for Ready

As you can probably guess TestKit waited for 3 seconds for a Ready message to be sent back.

To fix the test we add the the following to the Server Actor implementation:

package batey.akka.testing.backtosender
import akka.actor.Actor
import batey.akka.testing.backtosender.Messages.{Ready, Startup}
class Server extends Actor {
def receive: Actor.Receive = {
case Startup => {
sender ! Ready
}
case msg @ _ => {
println("I should really send something back!")
}
}
}
case object Messages {
case object Startup
case object Ready
}
view raw Server.scala hosted with ❤ by GitHub
And now the test will pass! The two important things to take note of are that our test case extended from TestKit, this gives you an ActorSystem. And that the test case mixed in the ImplicitSender trait, this allows us to receive messages use the methods like "expectMsg" to assert that the correct message has been received.

No comments: