scalaakkaakka-testkitakka-typedakka-actor

Akka Typed - How to sent Terminated message to BehaviorTestKit


I'm trying to unit test my actor's handling of a "Terminated" message for a child actor. The code under test is something like this:

    case Terminated(terminatedActor) =>
      val actorName = terminatedActor.path.name
      if (actorName.startsWith("ChildActor")) {
        doSomething()
      }
      Behaviors.same

In my unit test, I'm trying to do something like this:

    val testInbox = TestInbox[ParentActor.Request](name = "ChildActor1")
    val testKit = BehaviorTestKit(ParentActor())
    testKit.run(Terminated(testInbox.ref))
    assert( *** that doSomething() happened *** )

The unit test code doesn't compile. I get this error in the call to testKit.run():

type mismatch; found : akka.actor.typed.Terminated required: ParentActor.Request

I assume that this is because the Terminated message does not inherit from my ParentActor.Request trait.

Based on a below comment, I changed the unit test to:

    val testInbox = TestInbox[ParentActor.Request](name = "ChildActor1")
    val testKit = BehaviorTestKit(ParentActor())
    testKit.signal(Terminated(testInbox.ref))
    assert( *** that doSomething() happened *** )

This now compiles, but the call to testKit.signal() now throws a DeathPactException, which the docs say means that the actor is not handling the Terminated message, even though my production code definitely does handle it.

Any idea what is wrong?


Solution

  • Are you sure that your production code definitely handles the Terminated signal?

    Signals are not, from the perspective of a typed Behavior, messages. They are processed by the signal handler installed by receiveSignal. That signal handler takes not just the signal but the ActorContext as well, wrapped up in a tuple. If the response to a Terminated signal doesn't require the context, you still have to match against it:

    // inside a .receiveSignal...
    case (_, Terminated(terminatedActor)) =>
      val actorName = terminatedActor.path.name
      if (actorName.startsWith("ChildActor")) {
        doSomething()
      }
      Behaviors.same
    

    Note that Akka's test suite includes this test which exercises handling the Terminated signal when sent via testKit.signal:

          val other = TestInbox[String]()
          val testkit = BehaviorTestKit[Parent.Command](Parent.init)
          noException should be thrownBy {
            testkit.signal(Terminated(other.ref))
          }