Home > Enterprise >  Akka Typed - How to sent Terminated message to BehaviorTestKit
Akka Typed - How to sent Terminated message to BehaviorTestKit

Time:04-26

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?

CodePudding user response:

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))
      }
  • Related