diff --git a/akka-testkit-typed/src/main/scala/akka/testkit/typed/BehaviorTestkit.scala b/akka-testkit-typed/src/main/scala/akka/testkit/typed/BehaviorTestkit.scala index 220bc42c9e..146a831ebd 100644 --- a/akka-testkit-typed/src/main/scala/akka/testkit/typed/BehaviorTestkit.scala +++ b/akka-testkit-typed/src/main/scala/akka/testkit/typed/BehaviorTestkit.scala @@ -5,23 +5,17 @@ package akka.testkit.typed import java.util.concurrent.ConcurrentLinkedQueue +import akka.actor.typed.internal.ControlledExecutor +import akka.actor.typed.{ ActorRef, Behavior, PostStop, Props, Signal } +import akka.annotation.{ ApiMayChange, InternalApi } + import scala.annotation.tailrec import scala.collection.immutable -import scala.concurrent.duration.Duration -import scala.concurrent.duration.FiniteDuration +import scala.concurrent.duration.{ Duration, FiniteDuration } import scala.language.existentials import scala.util.control.Exception.Catcher import scala.util.control.NonFatal -import akka.actor.typed.internal.ControlledExecutor -import akka.actor.typed.ActorRef -import akka.actor.typed.Behavior -import akka.actor.typed.PostStop -import akka.actor.typed.Props -import akka.actor.typed.Signal -import akka.annotation.ApiMayChange -import akka.annotation.InternalApi - /** * All tracked effects must extend implement this type. It is deliberately * not sealed in order to allow extensions. @@ -178,8 +172,16 @@ class BehaviorTestkit[T] private (_name: String, _initialBehavior: Behavior[T]) } private var current = Behavior.validateAsInitial(Behavior.start(_initialBehavior, ctx)) + private var currentUncanonical = _initialBehavior def currentBehavior: Behavior[T] = current + + /** + * Returns the current behavior as it was returned from processing the previous message. + * For example if [[Behavior.unhandled]] is returned it will be kept here, but not in + * [[currentBehavior]]. + */ + def returnedBehavior: Behavior[T] = currentUncanonical def isAlive: Boolean = Behavior.isAlive(current) private def handleException: Catcher[Unit] = { @@ -196,7 +198,8 @@ class BehaviorTestkit[T] private (_name: String, _initialBehavior: Behavior[T]) */ def run(msg: T): Unit = { try { - current = Behavior.canonicalize(Behavior.interpretMessage(current, ctx, msg), current, ctx) + currentUncanonical = Behavior.interpretMessage(current, ctx, msg) + current = Behavior.canonicalize(currentUncanonical, current, ctx) ctx.executionContext match { case controlled: ControlledExecutor ⇒ controlled.runAll() case _ ⇒ @@ -209,7 +212,8 @@ class BehaviorTestkit[T] private (_name: String, _initialBehavior: Behavior[T]) */ def signal(signal: Signal): Unit = { try { - current = Behavior.canonicalize(Behavior.interpretSignal(current, ctx, signal), current, ctx) + currentUncanonical = Behavior.interpretSignal(current, ctx, signal) + current = Behavior.canonicalize(currentUncanonical, current, ctx) } catch handleException } diff --git a/akka-testkit-typed/src/test/scala/akka/testkit/typed/BehaviorTestkitSpec.scala b/akka-testkit-typed/src/test/scala/akka/testkit/typed/BehaviorTestkitSpec.scala index 2cb06a56f2..df7bc03005 100644 --- a/akka-testkit-typed/src/test/scala/akka/testkit/typed/BehaviorTestkitSpec.scala +++ b/akka-testkit-typed/src/test/scala/akka/testkit/typed/BehaviorTestkitSpec.scala @@ -130,4 +130,13 @@ class BehaviorTestkitSpec extends WordSpec with Matchers { effects shouldBe Seq(SpawnedAdapter) } } + + "BehaviorTestkit's run" can { + "run behaviors with messages without canonicalization" in { + val testkit = BehaviorTestkit[Father.Command](Father.init()) + testkit.run(SpawnAdapterWithName("adapter")) + testkit.currentBehavior should not be Behavior.same + testkit.returnedBehavior shouldBe Behavior.same + } + } }