Search code examples
scalaakkaactorakka-testkitakka-actor

Testing message forwarding in akka.testkit.TestKit


I want to test the following scenario:

Assume I have a parent actor, which creates two child actors like this.

class A extends Actor {
  def getActorOf(props: Props) = {
    context.actorOf(props, props.clazz.getTypeName)
  }

  def receive: Receive = {
    case "ping" => {
      val bChild = getActorOf(Props[B])
      val cChild = getActorOf(Props[C])
      Seq(bChild, cChild)
        .foreach(child => child ! "ping forwarded")
    }
  }
}

I want to test that if in case the parent get 'ping' he will send 'ping forwarded' message to both own children.

Is it possible to do this with TestKit?


Solution

  • Something like this, perhaps?

     class TestMe extends A {
       val (probeB, probeC) = (TestProbe(), TestProbe())
       override def getActorOf(props: Props) = props match {
         case Props(_, classOf[B], _) => probeB.ref
         case Props(_, classOf[C], _) => probeC.ref
       }
     }
    
     val fixture = TestActorRef[TestMe](Props[TestMe])
     fixture ! "ping" 
     fixture.underlyingActor.probeB.expectMsg("ping forwarded")
     fixture.underlyingActor.probeB.expectMsg("ping forwarded")
    

    Personally, I prefer a more "traditional" approach, whenever possible:

     trait Forwarder {
       def startAndForward[T : ClassTag](message: Any)(implicit context: ActorContext) = {
         val actor = context.actorOf(Props[T])
         actor ! message
         actor
      }
    }
    object Forwarder extends Forwarder
    
     class A(f: Forwarder = Forwarder) extends Actor {
    
       def receive: Receive = {
         case m@"ping" => 
           f.startAndForward[B]("ping forwarded")     
           f.startAndForward[C]("ping forwarded")
           sender ! "pong"
       }
     }
    

    Now, you can run your test in straightforward way:

     val fwd = mock[Forwarder]
     val fixture = context.actorOf(Props(new A(fwd)))
     fixture.ask("ping").futureValue shouldBe "pong"
     verify(fwd).startAndForward[B](ArgumentMatchers.eq("ping forwarded"))(any, any)
     verify(fwd).startAndForward[C](ArgumentMatchers.eq("ping forwarded"))(any, any)