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)
}
}
Inject
Props
for the children (e.g.HeartBeatAcceptWorker.props
) in the constructor of the parentHeartBeatPumpWorker
. Pass anyProps
you want from the test. Let the parent instantiate the children via providedProps
. Interact with the children. The last part is dependent on your data flow. For instance if the parent shields you from the children, but delegates messages to them, send the message to the parent. If children talk to each other use test probes or something similar.The technique I'm currently using is to intercept actor creation and create TestProbes. In my actors I mix in a separate ActorMaker trait:
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:
And I mix it in during tests
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]