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 0f5f5ac6c8..4ed6e3606f 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 @@ -27,6 +27,10 @@ object ActorTestKit { * 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 create(): ActorTestKit = new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]))) @@ -38,10 +42,26 @@ object ActorTestKit { * 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 create(name: String): ActorTestKit = new ActorTestKit(scaladsl.ActorTestKit(name)) + /** + * Create a testkit named from the class that is calling this method, + * 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. + * When the test has completed you should terminate the `ActorSystem` and + * the testkit with [[ActorTestKit#shutdownTestKit]]. + */ + def create(customConfig: Config): ActorTestKit = + new ActorTestKit(scaladsl.ActorTestKit(TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), customConfig)) + /** * Create a named testkit, and use a custom config for the actor system. * @@ -95,6 +115,11 @@ object ActorTestKit { shutdown(system, settings.DefaultActorSystemShutdownTimeout.asJava, settings.ThrowOnShutdownTimeout) } + /** + * Config loaded from `application-test.conf`, which is used if no specific config is given. + */ + def applicationTestConfig: Config = scaladsl.ActorTestKit.ApplicationTestConfig + } /** 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 b6238cde9f..952a98f376 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 @@ -39,9 +39,19 @@ import org.junit.rules.ExternalResource * } * } * }}} + * + * By default config is 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. + * A specific configuration can be passed as constructor parameter. */ final class TestKitJunitResource(_kit: ActorTestKit) extends ExternalResource { + /** + * 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 this() = this(ActorTestKit.create(TestKitUtils.testNameFromCallStack(classOf[TestKitJunitResource]))) /** 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 df3151f7b1..f8819da131 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 @@ -32,11 +32,15 @@ object ActorTestKit { * 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(): ActorTestKit = new ActorTestKit( name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), - config = noConfigSet, + config = ApplicationTestConfig, settings = None) /** @@ -46,9 +50,28 @@ object ActorTestKit { * 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 = noConfigSet, settings = None) + 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. + * + * 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]]. + */ + def apply(customConfig: Config): ActorTestKit = + new ActorTestKit( + name = TestKitUtils.testNameFromCallStack(classOf[ActorTestKit]), + config = customConfig, + settings = None) /** * Create a named testkit, and use a custom config for the actor system. @@ -89,9 +112,10 @@ object ActorTestKit { def shutdown(system: ActorSystem[_], timeout: Duration, throwIfShutdownFails: Boolean = false): Unit = TestKitUtils.shutdown(system, timeout, throwIfShutdownFails) - // place holder for no custom config specified to avoid the boilerplate - // of an option for config in the trait - private val noConfigSet = ConfigFactory.parseString("") + /** + * Config loaded from `application-test.conf`, which is used if no specific config is given. + */ + val ApplicationTestConfig: Config = ConfigFactory.load("application-test") } @@ -116,8 +140,7 @@ final class ActorTestKit private[akka] (val name: String, val config: Config, se * INTERNAL API */ @InternalApi private[akka] val internalSystem: ActorSystem[ActorTestKitGuardian.TestKitCommand] = - if (config eq ActorTestKit.noConfigSet) ActorSystem(ActorTestKitGuardian.testKitGuardian, name) - else ActorSystem(ActorTestKitGuardian.testKitGuardian, name, config) + ActorSystem(ActorTestKitGuardian.testKitGuardian, name, config) implicit def system: ActorSystem[Nothing] = internalSystem 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 8f0ec56cc5..c73bd419bd 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 @@ -19,6 +19,11 @@ import org.scalatest.time.Span * * Note that ScalaTest is not provided as a transitive dependency of the testkit module but must be added explicitly * to your project to use this. + * + * By default config is 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. + * A specific configuration can be passed as constructor parameter. */ abstract class ScalaTestWithActorTestKit(testKit: ActorTestKit) extends ActorTestKitBase(testKit) @@ -28,6 +33,11 @@ abstract class ScalaTestWithActorTestKit(testKit: ActorTestKit) with ScalaFutures with Eventually { + /** + * 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 this() = this(ActorTestKit(ActorTestKitBase.testNameFromCallStack())) /** diff --git a/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java new file mode 100644 index 0000000000..6608362176 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package jdocs.akka.actor.testkit.typed.javadsl; + +// #default-application-conf +import com.typesafe.config.ConfigFactory; + +// #default-application-conf + +public class TestConfigExample { + + void illustrateApplicationConfig() { + + // #default-application-conf + ConfigFactory.load() + // #default-application-conf + ; + + // #parse-string + ConfigFactory.parseString("akka.loglevel = DEBUG \n" + "akka.log-config-on-start = on \n") + // #parse-string + ; + + // #fallback-application-conf + ConfigFactory.parseString("akka.loglevel = DEBUG \n" + "akka.log-config-on-start = on \n") + .withFallback(ConfigFactory.load()) + // #fallback-application-conf + ; + } +} diff --git a/akka-actor-testkit-typed/src/test/resources/application-test.conf b/akka-actor-testkit-typed/src/test/resources/application-test.conf new file mode 100644 index 0000000000..b52f74104b --- /dev/null +++ b/akka-actor-testkit-typed/src/test/resources/application-test.conf @@ -0,0 +1,2 @@ +# used by ActorTestKitSpec +test.from-application-test = yes diff --git a/akka-actor-testkit-typed/src/test/resources/application.conf b/akka-actor-testkit-typed/src/test/resources/application.conf new file mode 100644 index 0000000000..c557fa2abc --- /dev/null +++ b/akka-actor-testkit-typed/src/test/resources/application.conf @@ -0,0 +1,2 @@ +# used by ActorTestKitSpec +test.from-application = yes 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 1934756671..78fd001a4b 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,9 +5,10 @@ package akka.actor.testkit.typed.scaladsl import akka.Done - import scala.concurrent.Promise + import akka.actor.typed.scaladsl.Behaviors +import com.typesafe.config.ConfigFactory import org.scalatest.BeforeAndAfterAll import org.scalatest.Matchers import org.scalatest.WordSpec @@ -62,6 +63,29 @@ class ActorTestKitSpec extends ScalaTestWithActorTestKit with WordSpecLike { testkit2.shutdownTestKit() testkit2.system.whenTerminated.futureValue shouldBe a[Done] } + + "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) + } + } } diff --git a/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala new file mode 100644 index 0000000000..5118abf4c0 --- /dev/null +++ b/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package docs.akka.actor.testkit.typed.scaladsl + +object TestConfigExample { + + def illustrateApplicationConfig(): Unit = { + + //#default-application-conf + import com.typesafe.config.ConfigFactory + + ConfigFactory.load() + //#default-application-conf + + //#parse-string + ConfigFactory.parseString(""" + akka.loglevel = DEBUG + akka.log-config-on-start = on + """) + //#parse-string + + //#fallback-application-conf + ConfigFactory.parseString(""" + akka.loglevel = DEBUG + akka.log-config-on-start = on + """).withFallback(ConfigFactory.load()) + //#fallback-application-conf + } +} diff --git a/akka-docs/src/main/paradox/general/configuration.md b/akka-docs/src/main/paradox/general/configuration.md index 252fa995ae..57c077be3e 100644 --- a/akka-docs/src/main/paradox/general/configuration.md +++ b/akka-docs/src/main/paradox/general/configuration.md @@ -58,7 +58,8 @@ to `application`—may be overridden using the `config.resource` property If you are writing an Akka application, keep your configuration in `application.conf` at the root of the class path. If you are writing an Akka-based library, keep its configuration in `reference.conf` at the root -of the JAR file. +of the JAR file. It's not supported to override a config property owned by +one library in a `reference.conf` of another library. @@@ diff --git a/akka-docs/src/main/paradox/typed/testing.md b/akka-docs/src/main/paradox/typed/testing.md index d1cb69b230..f5b4a9458f 100644 --- a/akka-docs/src/main/paradox/typed/testing.md +++ b/akka-docs/src/main/paradox/typed/testing.md @@ -166,6 +166,50 @@ Scala Java : @@snip [AsyncTestingExampleTest.java](/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/JunitIntegrationExampleTest.java) { #junit-integration } +### Configuration + +By default the `ActorTestKit` loads configuration from `application-test.conf` if that exists, otherwise +it is 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. +A specific configuration can be given as parameter when creating the TestKit. + +If you prefer to use `application.conf` you can pass that as the configuration parameter to the TestKit. +It's loaded with: + +Scala +: @@snip [TestConfigExample.scala](/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala) { #default-application-conf } + +Java +: @@snip [TestConfigExample.java](/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java) { #default-application-conf } + +It's often convenient to define configuration for a specific test as a `String` in the test itself and +use that as the configuration parameter to the TestKit. `ConfigFactory.parseString` can be used for that: + +Scala +: @@snip [TestConfigExample.scala](/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala) { #parse-string } + +Java +: @@snip [TestConfigExample.java](/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java) { #parse-string } + +Combining those approaches using `withFallback`: + +Scala +: @@snip [TestConfigExample.scala](/akka-actor-testkit-typed/src/test/scala/docs/akka/actor/testkit/typed/scaladsl/TestConfigExample.scala) { #fallback-application-conf } + +Java +: @@snip [TestConfigExample.java](/akka-actor-testkit-typed/src/test/java/jdocs/akka/actor/testkit/typed/javadsl/TestConfigExample.java) { #fallback-application-conf } + + +More information can be found in the [documentation of the configuration library](https://github.com/lightbend/config#using-the-library). + +@@@ note + +Note that `reference.conf` files are intended for libraries to define default values and shouldn't be used +in an application. It's not supported to override a config property owned by one library in a `reference.conf` +of another library. + +@@@ + ### Controlling the scheduler It can be hard to reliably unit test specific scenario's when your actor relies on timing: