Fix 27338 - Allow passing an actor system to the typed.ActorTestKit (#28871)

* Add apply methods for typed ActorTestKit allowing for passing an ActorSystem
This commit is contained in:
Robert Eklund 2020-04-08 02:43:18 -05:00 committed by GitHub
parent a26d7ef91d
commit 6d843efbf8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 60 deletions

View file

@ -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")

View file

@ -21,10 +21,8 @@ import akka.util.JavaDurationConverters._
object ActorTestKit { 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 * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
* *
@ -36,7 +34,19 @@ object ActorTestKit {
new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the name. * e.g. threads will include the name.
@ -51,11 +61,11 @@ object ActorTestKit {
new ActorTestKit(scaladsl.ActorTestKit(name)) 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. * and use a custom config for the actor system.
* *
* It will create an [[akka.actor.typed.ActorSystem]] with this name, * It will also used the provided customConfig provided to create the `ActorSystem`
* e.g. threads will include the name. *
* When the test has completed you should terminate the `ActorSystem` and * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */
@ -63,10 +73,14 @@ object ActorTestKit {
new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), customConfig)) 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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the 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 * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */
@ -74,11 +88,14 @@ object ActorTestKit {
new ActorTestKit(scaladsl.ActorTestKit(name, customConfig)) new ActorTestKit(scaladsl.ActorTestKit(name, customConfig))
/** /**
* Create a named testkit, and use a custom config for the actor system, * Create an [[akka.actor.typed.ActorSystem]] named based on the provided name,
* and a custom [[akka.actor.testkit.typed.TestKitSettings]] * 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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the 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 * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */

View file

@ -54,6 +54,11 @@ final class TestKitJunitResource(_kit: ActorTestKit) extends ExternalResource {
*/ */
def this() = this(ActorTestKit.create(TestKitUtils.testNameFromCallStack(classOf[TestKitJunitResource]))) 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. * Use a custom config for the actor system.
*/ */

View file

@ -26,7 +26,39 @@ import org.slf4j.LoggerFactory
object ActorTestKit { 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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the 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. * 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. * The application.conf of your project is not used in this case.
*/ */
def apply(): ActorTestKit = def apply(name: String): ActorTestKit = {
new ActorTestKit( val system =
name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), ActorSystem(ActorTestKitGuardian.testKitGuardian, TestKitUtils.scrubActorSystemName(name), ApplicationTestConfig)
config = ApplicationTestConfig, new ActorTestKit(system, system, settings = None)
settings = None) }
/** /**
* Create a named testkit. * 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]].
*
* 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,
* and use a custom config for the actor system. * and use a custom config for the actor system.
* *
* It will create an [[akka.actor.typed.ActorSystem]] with this name, * It will also used the provided customConfig provided to create the `ActorSystem`
* e.g. threads will include the name. *
* When the test has completed you should terminate the `ActorSystem` and * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */
def apply(customConfig: Config): ActorTestKit = def apply(customConfig: Config): ActorTestKit = {
new ActorTestKit( val system = ActorSystem(
name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), ActorTestKitGuardian.testKitGuardian,
config = customConfig, TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]),
settings = None) 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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the 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 * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */
def apply(name: String, customConfig: Config): ActorTestKit = def apply(name: String, customConfig: Config): ActorTestKit = {
new ActorTestKit(name = TestKitUtils.scrubActorSystemName(name), config = customConfig, settings = None) 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, * Create an [[akka.actor.typed.ActorSystem]] named based on the provided name,
* and a custom [[akka.actor.testkit.typed.TestKitSettings]] * 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, * It will create an [[akka.actor.typed.ActorSystem]] with this name,
* e.g. threads will include the 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 * When the test has completed you should terminate the `ActorSystem` and
* the testkit with [[ActorTestKit#shutdownTestKit]]. * the testkit with [[ActorTestKit#shutdownTestKit]].
*/ */
def apply(name: String, customConfig: Config, settings: TestKitSettings): ActorTestKit = def apply(name: String, customConfig: Config, settings: TestKitSettings): ActorTestKit = {
new ActorTestKit(name = TestKitUtils.scrubActorSystemName(name), config = customConfig, settings = Some(settings)) 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, * 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]] * 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 // 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 = implicit def testKitSettings: TestKitSettings =
settings.getOrElse(TestKitSettings(system)) settings.getOrElse(TestKitSettings(system))
@ -142,9 +181,6 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se
/** /**
* INTERNAL API * INTERNAL API
*/ */
@InternalApi private[akka] val internalSystem: ActorSystem[ActorTestKitGuardian.TestKitCommand] =
ActorSystem(ActorTestKitGuardian.testKitGuardian, name, config)
implicit def system: ActorSystem[Nothing] = internalSystem implicit def system: ActorSystem[Nothing] = internalSystem
private val childName: Iterator[String] = Iterator.from(0).map(_.toString) 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 * guardian
*/ */
def spawn[T](behavior: Behavior[T], props: Props): ActorRef[T] = 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 * 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 * guardian
*/ */
def spawn[T](behavior: Behavior[T], name: String, props: Props): ActorRef[T] = 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. * 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 = def stop[T](ref: ActorRef[T], max: FiniteDuration = timeout.duration): Unit =
try { try {
Await.result(internalSystem.ask { x: ActorRef[ActorTestKitGuardian.Ack.type] => Await.result(internalTestKitGuardian.ask { x: ActorRef[ActorTestKitGuardian.Ack.type] =>
ActorTestKitGuardian.StopActor(ref, x) ActorTestKitGuardian.StopActor(ref, x)
}, max) }, max)
} catch { } catch {

View file

@ -5,6 +5,7 @@
package akka.actor.testkit.typed.scaladsl package akka.actor.testkit.typed.scaladsl
import akka.actor.testkit.typed.TestKitSettings import akka.actor.testkit.typed.TestKitSettings
import akka.actor.typed.ActorSystem
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import org.scalatest.{ BeforeAndAfterAll, TestSuite } import org.scalatest.{ BeforeAndAfterAll, TestSuite }
@ -41,6 +42,11 @@ abstract class ScalaTestWithActorTestKit(testKit: ActorTestKit)
*/ */
def this() = this(ActorTestKit(ActorTestKitBase.testNameFromCallStack())) 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. * Use a custom config for the actor system.
*/ */

View file

@ -5,8 +5,10 @@
package akka.actor.testkit.typed.scaladsl package akka.actor.testkit.typed.scaladsl
import akka.Done 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 akka.actor.typed.scaladsl.Behaviors
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import org.scalatest.BeforeAndAfterAll import org.scalatest.BeforeAndAfterAll
@ -21,6 +23,16 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with AnyWordSpecLike wi
system.name should ===("ActorTestKitSpec") 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 { "generate a default name from the test class" in {
val testkit2 = ActorTestKit() val testkit2 = ActorTestKit()
try { try {
@ -64,23 +76,18 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with AnyWordSpecLike wi
} }
"load application-test.conf by default" in { "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.getString("test.from-application-test") should ===("yes")
testKit.system.settings.config.hasPath("test.from-application") should ===(false) testKit.system.settings.config.hasPath("test.from-application") should ===(false)
} }
"not load application-test.conf if specific Config given" in { "not load application-test.conf if specific Config given" in {
val testKit2 = ActorTestKit(ConfigFactory.parseString("test.specific-config = yes")) 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.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-test") should ===(false)
testKit2.system.settings.config.hasPath("test.from-application") should ===(false) testKit2.system.settings.config.hasPath("test.from-application") should ===(false)
// same if via ScalaTestWithActorTestKit // same if via ScalaTestWithActorTestKit
val scalaTestWithActorTestKit2 = new ScalaTestWithActorTestKit("test.specific-config = yes") {} 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.system.settings.config.hasPath("test.from-application-test") should ===(false)
scalaTestWithActorTestKit2.testKit.system.settings.config.hasPath("test.from-application") should ===(false) scalaTestWithActorTestKit2.testKit.system.settings.config.hasPath("test.from-application") should ===(false)
} }