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.
In Akka, you can stop Actors by invoking the stop() method of either ActorContext or ActorSystem class. ActorContext is used to stop child actor and ActorSystem is used to stop top level Actor.
Behind the scenes Akka will run sets of actors on sets of real threads, where typically many actors share one thread, and subsequent invocations of one actor may end up being processed on different threads.
When an actor throws an unexpected exception, a failure, while processing a message or during initialization, the actor will by default be stopped.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With