2
votes

I am trying to understand the akka-Testkit", and hope it is ok to ask about it.

I found some tutorials and blogs that either access a state- or a lastMsg- attribute on the underlyingActor on the TestActorRef. However, a TestActorRef from the the "akka-testkit_2.11" % "2.4.10" does not have these attributes. I looked at the example on the akka website, and maybe I am missing something, but they show testing of among other an echo actor, but not with any simple actor implementations.

So, could someone help me understand how to test a worker that will respond with the same number if n % 3 == 0 (which is the case in the example). I would prefer not to use a future and the ask pattern if possible, and would like to make a test on the response that the actor will give (from that actors perspective by accessing its state or something similar).

class ProjectEulerScalaTestAkka extends TestKit(ActorSystem("testing")) with WordSpecLike with MustMatchers {

    "A simple actor" must {
        val actorRef = TestActorRef[Worker]
        "receive messages" in {
             actorRef ! 3
             actorRef.underlyingActor.state//must not equal("world")
        }
    }
}

related: How do I test an Akka actor that sends a message to another actor?

For now I am using a synchronized testing approach;

 import akka.actor.ActorSystem
 import akka.testkit.{TestActorRef, TestKit}
 import org.scalatest.Matchers
 import org.scalatest.WordSpecLike
 import akka.pattern.ask
 import scala.concurrent.Future
 import scala.concurrent.duration._
 import scala.util.Success

class ProjectEulerScalaTestAkka extends TestKit(ActorSystem("testing")) with WordSpecLike with Matchers {
      implicit val time = akka.util.Timeout(100 seconds)
      "A simple actor" must {
           val actorRef = TestActorRef[Worker]
           "receive messages" in {
               val f = (actorRef ? 3).asInstanceOf[Future[Int]]
               val reply = f.value.get
               reply should equal (Success(3))
           }
      }
}
1

1 Answers

0
votes

What I did was mock the interface of the Actor that I was sending the message to, capturing the message and sending back a success message to the testActor reference.

success object with the captured message payload

case class SuccessWith(capturedMessage:Any = null)

a mock actor that you send your message to which, in turn, returns some value to the test actor

case class MockActor(requester: ActorRef) extends Actor {
  override def receive: Receive = {
    case i: Int => {
      requester ! i
    }
  }
}

set up the actor you're wanting to unit test

val actorRef = system.actorOf(Props(new YourActor(args)))

and then your test

  "A simple actor" must {
       "receive messages" in {
           val f = actorRef ! 3
           expectMsg(Success(3))
       }
  }
}