diff --git a/akka-actor-testkit-typed/src/main/mima-filters/2.6.4.backwards.excludes/27338-ActorTestKit-system.excludes b/akka-actor-testkit-typed/src/main/mima-filters/2.6.4.backwards.excludes/27338-ActorTestKit-system.excludes new file mode 100644 index 0000000000..717e6c82a5 --- /dev/null +++ b/akka-actor-testkit-typed/src/main/mima-filters/2.6.4.backwards.excludes/27338-ActorTestKit-system.excludes @@ -0,0 +1,3 @@ +# #27338 allow passing ActorSystem to ActorTestKit +ProblemFilters.exclude[IncompatibleSignatureProblem]("akka.actor.testkit.typed.scaladsl.ActorTestKit.internalSystem") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.actor.testkit.typed.scaladsl.ActorTestKit.this") \ No newline at end of file diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/ActorTestKit.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/ActorTestKit.scala index e1a224441e..f4061114a8 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/ActorTestKit.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/ActorTestKit.scala @@ -21,10 +21,8 @@ import akka.util.JavaDurationConverters._ object ActorTestKit { /** - * Create a testkit named from the class that is calling this method. + * Create a testkit named from the ActorTestKit class. * - * It will create an [[akka.actor.typed.ActorSystem]] with this name, - * e.g. threads will include the name. * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. * @@ -36,7 +34,19 @@ object ActorTestKit { new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]))) /** - * Create a named testkit. + * Create a testkit from the provided actor system. + * + * When the test has completed you should terminate the `ActorSystem` and + * the testkit with [[ActorTestKit#shutdownTestKit]]. + * + * Config loaded from the provided actor if that exists, otherwise + * using default configuration from the reference.conf resources that ship with the Akka libraries. + */ + def create(system: ActorSystem[_]): ActorTestKit = + new ActorTestKit(scaladsl.ActorTestKit(system)) + + /** + * Create a testkit using the provided name. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. @@ -51,11 +61,11 @@ object ActorTestKit { new ActorTestKit(scaladsl.ActorTestKit(name)) /** - * Create a testkit named from the class that is calling this method, + * Create a testkit named from the ActorTestKit class, * and use a custom config for the actor system. * - * It will create an [[akka.actor.typed.ActorSystem]] with this name, - * e.g. threads will include the name. + * It will also used the provided customConfig provided to create the `ActorSystem` + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ @@ -63,10 +73,14 @@ object ActorTestKit { new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), customConfig)) /** - * Create a named testkit, and use a custom config for the actor system. + * Create a test kit named based on the provided name, + * and uses the provided custom config for the actor system. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. + * + * It will also used the provided customConfig provided to create the `ActorSystem` + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ @@ -74,11 +88,14 @@ object ActorTestKit { new ActorTestKit(scaladsl.ActorTestKit(name, customConfig)) /** - * Create a named testkit, and use a custom config for the actor system, - * and a custom [[akka.actor.testkit.typed.TestKitSettings]] + * Create an [[akka.actor.typed.ActorSystem]] named based on the provided name, + * use the provided custom config for the actor system, and the testkit will use the provided setting. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. + * + * It will also used the provided customConfig provided to create the `ActorSystem`, and provided setting. + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/TestKitJunitResource.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/TestKitJunitResource.scala index ff99e316b7..15a5b59f30 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/TestKitJunitResource.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/javadsl/TestKitJunitResource.scala @@ -54,6 +54,11 @@ final class TestKitJunitResource(_kit: ActorTestKit) extends ExternalResource { */ def this() = this(ActorTestKit.create(TestKitUtils.testNameFromCallStack(classOf[TestKitJunitResource]))) + /** + * Use a custom [[akka.actor.typed.ActorSystem]] for the actor system. + */ + def this(system: ActorSystem[_]) = this(ActorTestKit.create(system)) + /** * Use a custom config for the actor system. */ diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ActorTestKit.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ActorTestKit.scala index 7dbf9c34c2..ccee21899a 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ActorTestKit.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ActorTestKit.scala @@ -26,7 +26,39 @@ import org.slf4j.LoggerFactory object ActorTestKit { /** - * Create a testkit named from the class that is calling this method. + * Create a testkit named from the ActorTestKit class. + * + * When the test has completed you should terminate the `ActorSystem` and + * the testkit with [[ActorTestKit#shutdownTestKit]]. + * + * Config loaded from `application-test.conf` if that exists, otherwise + * using default configuration from the reference.conf resources that ship with the Akka libraries. + * The application.conf of your project is not used in this case. + */ + def apply(): ActorTestKit = { + val system = ActorSystem( + ActorTestKitGuardian.testKitGuardian, + TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), + ApplicationTestConfig) + new ActorTestKit(system, system, settings = None) + } + + /** + * Create a testkit from the provided actor system. + * + * When the test has completed you should terminate the `ActorSystem` and + * the testkit with [[ActorTestKit#shutdownTestKit]]. + * + * Config loaded from the provided actor if that exists, otherwise + * using default configuration from the reference.conf resources that ship with the Akka libraries. + */ + def apply(system: ActorSystem[_]): ActorTestKit = { + val testKitGuardian = system.systemActorOf(ActorTestKitGuardian.testKitGuardian, "test") + new ActorTestKit(system, testKitGuardian, settings = None) + } + + /** + * Create a testkit using the provided name. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. @@ -37,64 +69,64 @@ object ActorTestKit { * using default configuration from the reference.conf resources that ship with the Akka libraries. * The application.conf of your project is not used in this case. */ - def apply(): ActorTestKit = - new ActorTestKit( - name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), - config = ApplicationTestConfig, - settings = None) + def apply(name: String): ActorTestKit = { + val system = + ActorSystem(ActorTestKitGuardian.testKitGuardian, TestKitUtils.scrubActorSystemName(name), ApplicationTestConfig) + new ActorTestKit(system, system, settings = None) + } /** - * Create a named testkit. - * - * It will create an [[akka.actor.typed.ActorSystem]] with this name, - * e.g. threads will include the name. - * When the test has completed you should terminate the `ActorSystem` and - * the testkit with [[ActorTestKit#shutdownTestKit]]. - * - * Config loaded from `application-test.conf` if that exists, otherwise - * using default configuration from the reference.conf resources that ship with the Akka libraries. - * The application.conf of your project is not used in this case. - */ - def apply(name: String): ActorTestKit = - new ActorTestKit(name = TestKitUtils.scrubActorSystemName(name), config = ApplicationTestConfig, settings = None) - - /** - * Create a testkit named from the class that is calling this method, + * Create a testkit named from the ActorTestKit class, * and use a custom config for the actor system. * - * It will create an [[akka.actor.typed.ActorSystem]] with this name, - * e.g. threads will include the name. + * It will also used the provided customConfig provided to create the `ActorSystem` + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ - def apply(customConfig: Config): ActorTestKit = - new ActorTestKit( - name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), - config = customConfig, - settings = None) + def apply(customConfig: Config): ActorTestKit = { + val system = ActorSystem( + ActorTestKitGuardian.testKitGuardian, + TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), + customConfig) + new ActorTestKit(system, system, settings = None) + } /** - * Create a named testkit, and use a custom config for the actor system. + * Create a test kit named based on the provided name, + * and uses the provided custom config for the actor system. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. + * + * It will also used the provided customConfig provided to create the `ActorSystem` + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ - def apply(name: String, customConfig: Config): ActorTestKit = - new ActorTestKit(name = TestKitUtils.scrubActorSystemName(name), config = customConfig, settings = None) + def apply(name: String, customConfig: Config): ActorTestKit = { + val system = + ActorSystem(ActorTestKitGuardian.testKitGuardian, TestKitUtils.scrubActorSystemName(name), customConfig) + new ActorTestKit(system, system, settings = None) + } /** - * Create a named testkit, and use a custom config for the actor system, - * and a custom [[akka.actor.testkit.typed.TestKitSettings]] + * Create an [[akka.actor.typed.ActorSystem]] named based on the provided name, + * use the provided custom config for the actor system, and the testkit will use the provided setting. * * It will create an [[akka.actor.typed.ActorSystem]] with this name, * e.g. threads will include the name. + * + * It will also used the provided customConfig provided to create the `ActorSystem`, and provided setting. + * * When the test has completed you should terminate the `ActorSystem` and * the testkit with [[ActorTestKit#shutdownTestKit]]. */ - def apply(name: String, customConfig: Config, settings: TestKitSettings): ActorTestKit = - new ActorTestKit(name = TestKitUtils.scrubActorSystemName(name), config = customConfig, settings = Some(settings)) + def apply(name: String, customConfig: Config, settings: TestKitSettings): ActorTestKit = { + val system = + ActorSystem(ActorTestKitGuardian.testKitGuardian, TestKitUtils.scrubActorSystemName(name), customConfig) + new ActorTestKit(system, system, settings = Some(settings)) + } /** * Shutdown the given [[akka.actor.typed.ActorSystem]] and block until it shuts down, @@ -131,10 +163,17 @@ object ActorTestKit { * * For synchronous testing of a `Behavior` see [[BehaviorTestKit]] */ -final class ActorTestKit private[akka] (val name: String, val config: Config, settings: Option[TestKitSettings]) { +final class ActorTestKit private[akka] ( + val internalSystem: ActorSystem[_], + internalTestKitGuardian: ActorRef[ActorTestKitGuardian.TestKitCommand], + settings: Option[TestKitSettings]) { + + val name = internalSystem.name + + val config = internalSystem.settings.config // avoid slf4j noise by touching it first from single thread #28673 - LoggerFactory.getLogger(name).debug("Starting ActorTestKit") + LoggerFactory.getLogger(internalSystem.name).debug("Starting ActorTestKit") implicit def testKitSettings: TestKitSettings = settings.getOrElse(TestKitSettings(system)) @@ -142,9 +181,6 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se /** * INTERNAL API */ - @InternalApi private[akka] val internalSystem: ActorSystem[ActorTestKitGuardian.TestKitCommand] = - ActorSystem(ActorTestKitGuardian.testKitGuardian, name, config) - implicit def system: ActorSystem[Nothing] = internalSystem private val childName: Iterator[String] = Iterator.from(0).map(_.toString) @@ -172,7 +208,9 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se * guardian */ def spawn[T](behavior: Behavior[T], props: Props): ActorRef[T] = - Await.result(internalSystem.ask(ActorTestKitGuardian.SpawnActorAnonymous(behavior, _, props)), timeout.duration) + Await.result( + internalTestKitGuardian.ask(ActorTestKitGuardian.SpawnActorAnonymous(behavior, _, props)), + timeout.duration) /** * Spawn the given behavior. This is created as a child of the test kit @@ -186,7 +224,9 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se * guardian */ def spawn[T](behavior: Behavior[T], name: String, props: Props): ActorRef[T] = - Await.result(internalSystem.ask(ActorTestKitGuardian.SpawnActor(name, behavior, _, props)), timeout.duration) + Await.result( + internalTestKitGuardian.ask(ActorTestKitGuardian.SpawnActor(name, behavior, _, props)), + timeout.duration) /** * Stop the actor under test and wait until it terminates. @@ -195,7 +235,7 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se */ def stop[T](ref: ActorRef[T], max: FiniteDuration = timeout.duration): Unit = try { - Await.result(internalSystem.ask { x: ActorRef[ActorTestKitGuardian.Ack.type] => + Await.result(internalTestKitGuardian.ask { x: ActorRef[ActorTestKitGuardian.Ack.type] => ActorTestKitGuardian.StopActor(ref, x) }, max) } catch { diff --git a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ScalaTestWithActorTestKit.scala b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ScalaTestWithActorTestKit.scala index bc1880f0ab..675615a094 100644 --- a/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ScalaTestWithActorTestKit.scala +++ b/akka-actor-testkit-typed/src/main/scala/akka/actor/testkit/typed/scaladsl/ScalaTestWithActorTestKit.scala @@ -5,6 +5,7 @@ package akka.actor.testkit.typed.scaladsl import akka.actor.testkit.typed.TestKitSettings +import akka.actor.typed.ActorSystem import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import org.scalatest.{ BeforeAndAfterAll, TestSuite } @@ -41,6 +42,11 @@ abstract class ScalaTestWithActorTestKit(testKit: ActorTestKit) */ def this() = this(ActorTestKit(ActorTestKitBase.testNameFromCallStack())) + /** + * Use a custom [[akka.actor.typed.ActorSystem]] for the actor system. + */ + def this(system: ActorSystem[_]) = this(ActorTestKit(system)) + /** * Use a custom config for the actor system. */ diff --git a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala index 064e335436..2ad475d2ba 100644 --- a/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala +++ b/akka-actor-testkit-typed/src/test/scala/akka/actor/testkit/typed/scaladsl/ActorTestKitSpec.scala @@ -5,8 +5,10 @@ package akka.actor.testkit.typed.scaladsl import akka.Done -import scala.concurrent.Promise +import akka.actor.testkit.typed.internal.ActorTestKitGuardian +import akka.actor.typed.ActorSystem +import scala.concurrent.Promise import akka.actor.typed.scaladsl.Behaviors import com.typesafe.config.ConfigFactory import org.scalatest.BeforeAndAfterAll @@ -21,6 +23,16 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with AnyWordSpecLike wi system.name should ===("ActorTestKitSpec") } + "generate a test kit from the provided actor system" in { + val config = ConfigFactory.parseString("test.specific-config = yes") + val system = ActorSystem(ActorTestKitGuardian.testKitGuardian, "TestActor", config) + val testkit2 = ActorTestKit(system) + try { + testkit2.internalSystem should ===(system) + testkit2.system should ===(system) + } finally testkit2.shutdownTestKit() + } + "generate a default name from the test class" in { val testkit2 = ActorTestKit() try { @@ -64,23 +76,18 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with AnyWordSpecLike wi } "load application-test.conf by default" in { - testKit.config.getString("test.from-application-test") should ===("yes") testKit.system.settings.config.getString("test.from-application-test") should ===("yes") testKit.system.settings.config.hasPath("test.from-application") should ===(false) } "not load application-test.conf if specific Config given" in { val testKit2 = ActorTestKit(ConfigFactory.parseString("test.specific-config = yes")) - testKit2.config.getString("test.specific-config") should ===("yes") testKit2.system.settings.config.getString("test.specific-config") should ===("yes") - testKit2.config.hasPath("test.from-application-test") should ===(false) testKit2.system.settings.config.hasPath("test.from-application-test") should ===(false) testKit2.system.settings.config.hasPath("test.from-application") should ===(false) // same if via ScalaTestWithActorTestKit val scalaTestWithActorTestKit2 = new ScalaTestWithActorTestKit("test.specific-config = yes") {} - scalaTestWithActorTestKit2.testKit.config.getString("test.specific-config") should ===("yes") - scalaTestWithActorTestKit2.testKit.config.hasPath("test.from-application-test") should ===(false) scalaTestWithActorTestKit2.system.settings.config.hasPath("test.from-application-test") should ===(false) scalaTestWithActorTestKit2.testKit.system.settings.config.hasPath("test.from-application") should ===(false) }