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)
}
}
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.
Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.
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]
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