0

I'm writing an Actor that should watch another Actor; let's call the latter one the target. My Actor should stop itself once its target is stopped. For this target I only have an ActorSelection. To watch it, I obviously need an ActorRef, so I figured I should send the ActorSelection an Identify message; when it replies back with ActorIdentity I would have its ActorRef. So far so good, but I can't get it to work.

Here's the spec:

// Arrange
val probe = TestProbe()
val target = TestProbe().ref
val sut = system.actorOf(MyActor.props(system.actorSelection(target.path)), "watch-target")
probe watch sut

// Act
target ! PoisonPill

// Assert
probe.expectTerminated(sut)

And the implementation (an FSM, details skipped):

log.debug("Asking target selection {} to identify itself; messageId={}", selection.toString(), messageId)
selection ! Identify(messageId)

when(Waiting) {
  case Event(ActorIdentity(`messageId`, Some(ref)), Queue(q)) =>
    log.info("Received identity for remote target: {}", ref)
    context.watch(ref)
    goto(NextState) using TargetFound(ref)
  case Event(ActorIdentity(`messageId`, None), Queue(q)) =>
    log.error("Could not find requested target {}", selection.toString())
    stop()
}

initialize()

Now, when I run my test, it is green because the system under test is indeed stopped. But the problem is it stops itself because it can't find its target using the aforementioned steps. The log file says:

Asking target selection ActorSelection[Anchor(akka://default/), Path(/system/testProbe-3)] to identify itself; messageId=871823258

Could not find requested target ActorSelection[Anchor(akka://default/), Path(/system/testProbe-3)]

Am I missing something obvious here? Maybe a TestProbe should not reveal its real identity? I even tried by instantiating a dummy Actor as target but the results are the same. Any clue?

mthmulders
  • 9,483
  • 4
  • 37
  • 54

2 Answers2

0

Turns out the answer is actually very simple: the test runs so fast that before MyActor sends the Identify message to the selection, the Actor behind the selection has already received its PoisonPill and thus is killed.

Adding a little Thread.sleep() before sending that PoisonPill fixed the issue.

mthmulders
  • 9,483
  • 4
  • 37
  • 54
0

The target actor is getting terminated before the identify request is being made. This is because Akka only guarantees order when sending messages between a given pair of actors.

If you add a thread.sleep above the following line, the identify request should succeed.

Thread.sleep(100)
// Act
target ! PoisonPill

Note that there may be better ways to code the test - sleeping the thread is not ideal.

Your watching actor should also handle the Terminated message of the target actor, as described here.

Swifter
  • 211
  • 1
  • 5