MaatDeamon MaatDeamon - 4 months ago 99
Scala Question

Akka Actor Test: Automatic reply with a TestProbe

I am trying to get a test probe to reply with an acknowledgement, whenever it receive any message .

I wrote the following code in my test but it does not work:

val chgtWriter = new TestProbe(system) {

def receive: Receive = {

case m => println("receive messagereplying with ACK"); sender() ! ACK

}

}


Is there a way to do that. The actor that is actually sending the message to the test probe is definitely running on another thread than the TestThread. Below you can see the full test as currently crafted.

feature("The changeSetActor periodically fetch new change set following a schedule") {


scenario("A ChangeSetActor fetch new changeset from a Fetcher Actor that return a full and an empty ChangeSet"){


Given("a ChangeSetActor with a schedule of fetching a message every 10 seconds, a ChangeFetcher and a ChangeWriter")

val chgtFetcher = TestProbe()

val chgtWriter = new TestProbe(system) {

def receive: Receive = {

case m => println("receive message {} replying with ACK"); sender() ! ACK

}

}
val fromTime = Instant.now().truncatedTo(ChronoUnit.SECONDS)
val chgtActor = system.actorOf(ChangeSetActor.props(chgtWriter.ref, chgtFetcher.ref, fromTime))

When("all are started")


Then("The Change Fetcher should receive at least 3 messages from the ChangeSetActor within 40 seconds")

var changesetSNum = 1

val received = chgtFetcher.receiveWhile( 40 seconds) {

case FetchNewChangeSet(m) => {

println(s"received: FetchNewChangeSet(${m}")

if (changesetSNum == 1) {
chgtFetcher.reply(NewChangeSet(changeSet1))
changesetSNum += 1
}
else
chgtFetcher.reply(NoAvailableChangeSet)
}

}

received.size should be (3)

}


}

The changeSetActor is fully tested and works. The test hang with the ChangeWriter. It never receive a message in the receive method.

EDIT1(Following @Jakko anser)

The Auto Pilots is as follow:


val probe = TestProbe()
probe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any): TestActor.AutoPilot =
msg match {
case "stop" ⇒ TestActor.NoAutoPilot
case x ⇒ **testActor.tell(x, sender)**; TestActor.KeepRunning
}
})



Although all the explanation given so far is way clear, what is confusing in the official example is the reference "testActor". Who is testActor here ? there is no variable declaration of that name at that point.

Answer

You can script your test probes using Auto Pilots. For example:

import akka.testkit._
val probe = TestProbe()
probe.setAutoPilot(new TestActor.AutoPilot {
  def run(sender: ActorRef, msg: Any): TestActor.AutoPilot = {
    println("receive messagereplying with ACK")
    sender ! ACK
    TestActor.KeepRunning
  }
})

In the above example, we set up a test probe with an automatic message handler, Auto Pilot. The auto pilot will be automatically triggered when the probe receives a message. In this example, the auto pilot will print a message and respond back to the sender.

After a message has been handled, the auto pilot can decide how the next incoming message will be handled. It can either set up a different auto pilot, reuse the existing auto pilot (TestActor.KeepRunning), or disable the auto pilot completely (TestActor.NoAutoPilot). In this example, the same auto pilot will be used for handling all the incoming messages.

You can still use test probe assertions as usual even with the auto pilot attached to the probe.