8
votes

I'm using ScalaTest with the Akka TestKit to write unit and integration tests for an actor I've coded to simply send a message to another actor without mutating any internal state. Take this for example:

class MyActor extends Actor {
  val anotherActor = context.actorOf(Props[AnotherActor])
  def receive: Receive = {
    case MyMessage => anotherActor ! AnotherMessage
  }
}

I want to write a test that confirms that anotherActor processed AnotherMessage as a consequence of MyActor processing MyMessage. The classic example is to use TestActorRef to get at the underlying actor and check for some internal state that should have been affected upon message receipt like so:

val testActorRef = TestActorRef(new MyActor)
"MyActor" should "change some internal state when it receives MyMessage" in {
  testActorRef ! MyMessage
  testActorRef.underlyingActor.someState shouldBe "altered"
}

But in my case I don't care about such state. In fact, I want to avoid holding any such state. TestProbe wasn't quite what I was looking for either since you still have to register aTestProbe.ref with the actor under test. For the most part, I've looked at all the examples in the Akka documentation on testing (http://doc.akka.io/docs/akka/snapshot/scala/testing.html) but haven't found anything suitable.

1

1 Answers

16
votes

There's probably a few ways to do this, I'll show you one that works when we have something similar to test. I still think TestActorRef, TestKit and TestProbe are the way to go. Considering the following structure:

case object MyMessage
case object AnotherMessage

class MyActor extends Actor {
  val anotherActor = createAnother
  def receive: Receive = {
    case MyMessage => anotherActor ! AnotherMessage
  }

  def createAnother = context.actorOf(Props[AnotherActor])
}

class AnotherActor extends Actor{
  def receive = {
    case _ =>
  }
}

The issue is that you have an actor instance creating a child actor and as part of your test you need to make sure that child gets a message even though you in your test don't have any control in the creation of that child. When we have this situation, we do something simple like the following (done using specs2, but should be able to create something similar in ScalaTest):

import akka.actor._
import akka.testkit._
import org.specs2.mutable.SpecificationLike
import org.specs2.runner.JUnitRunner
import org.junit.runner.RunWith

class MessageSendingSpec extends TestKit(ActorSystem("test")) with SpecificationLike{

  val probe = TestProbe()
  val myActor = TestActorRef(new MyActor{
    override def createAnother = probe.ref
  })


  "Sending MyMessage to an instance of MyActor" should{
    "pass AnotherMessage to the child AnotherActor" in {
      myActor ! MyMessage
      probe.expectMsg(AnotherMessage)
      success
    }
  }
}

The key there is that when creating the actor to test, I override the method that creates the child in order to supply my probe. It's crude, but also simple and effective.