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 {
/**
* 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]].
*/

View file

@ -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.
*/

View file

@ -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 {

View file

@ -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.
*/

View file

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