Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test that Akka actor was created in Scala

I'm trying to write a test that will verify that my actor below is creating a heartBeatExpireWorker and a heartBeatAccepter, but I have no idea how to do it.

First I was thinking I could use Mockhito mock or a spy in place of context and then verify that I called actorOf, but I can't figure out a way to inject the context without breaking the Akka testing framework.

Then, I was thinking that I could send an Identify message to the workers to verify that they exist. But it occurred to me that that wouldn't work either because the Akka TestKit doesn't seem to create children actors of an actor under test. It can only take in Testprobes that can stand in for neighboring actors.

class HeartBeatPumpWorker(chatService: ChatService, target: HeartBeatMessageCmd) extends Actor with ActorLogging with
WorkersReference {

  val heartBeatInterval = chatService.getHeartBeatInterval

  val tick = context.system.scheduler.schedule(0 millis, heartBeatInterval millis, self, SendHeartBeat(target))

  override def postStop() = tick.cancel()

  def receive = {
    case SendHeartBeat(command: HeartBeatMessageCmd) =>
      log.debug("Sending heartbeat")
      //Send heartbeat to GWT
      val userTarget = NetworkWorker.buildEventUserTarget(command.getEventCode, command.getUser)

      val uuid: String = UUID.randomUUID().toString
      val freshCommand = new HeartBeatMessageCmd(command.getUser, command.getEventCode, uuid, command.getUserSession)
      networkWorker ! NetworkBroadcast(userTarget, freshCommand)

      val heartBeatId: String = freshCommand.getUuid
      //create expirer
      val heartBeatExpireWorkerRef = context.actorOf(HeartBeatExpireWorker.props(chatService, freshCommand),
        HeartBeatExpireWorker.name(heartBeatId))
      val heartBeatAccepterRef = context
        .actorOf(HeartBeatAcceptWorker.props(chatService, freshCommand), HeartBeatAcceptWorker.name(heartBeatId))

      //record heartbeat
        chatService.saveSentHeartbeat(heartBeatId, freshCommand.getUserSession, freshCommand.getEventCode,
          freshCommand.getUser,
        freshCommand.getTimeCmdGenerated)
    case _ =>
      log.error("Pumper received unknown message.  This shouldn't happen " + sender.path.toString)
      self ! PoisonPill
  }

}


object HeartBeatPumpWorker {
  def name(eventCode: String, user: String, sessionId: String) = f"HeartBeatPumpWorker-$eventCode-$user-$sessionId"

  def path(eventCode: String, user: String, sessionId: String) : String = {
    EventWorker.Path + "/" + name(eventCode, user, sessionId)
  }

  def props(chatService: ChatService, heartBeatMsgCmd: HeartBeatMessageCmd) = {
    Props(classOf[HeartBeatPumpWorker], chatService, heartBeatMsgCmd)
  }
}
like image 476
HappyCoder86 Avatar asked Sep 03 '13 23:09

HappyCoder86


People also ask

How do I start an Akka actor?

In Akka you can't create an instance of an Actor using the new keyword. Instead, you create Actor instances using a factory spawn methods. Spawn does not return an actor instance, but a reference, akka.

Is Akka only for Scala?

Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.


1 Answers

The technique I'm currently using is to intercept actor creation and create TestProbes. In my actors I mix in a separate ActorMaker trait:

trait ActorMaker { this: Actor =>
  def makeActor(props: Props) = context.actorOf(props)
}

And use it in MyActor extends Actor with ActorMaker instead of context.actorOf.

For tests I have a TestProbeMaker that captures all created actors and their props:

trait TestProbeMaker { this: Actor =>
  val probes = ListBuffer.empty[(Props, TestProbe)]
  def makeActor(props: Props) = { val probe = TestProbe()
    probes += (props -> probe)
    probe.ref
  }
}

And I mix it in during tests

val actorUnderTest = TestActorRef(Props(new MyActor with TestProbeMaker))

That way I can assert exactly what actors are created. I can also use probe.expectMsg to assert that messages are sent to those created actors.

To access the probes use actorUnderTest.underlyingActor.asInstanceOf[TestProbeMaker]

like image 101
Andrejs Avatar answered Sep 26 '22 20:09

Andrejs