Use application-test.conf for typed testkit, #25708

* solves the problem that previously it loaded application.conf
  if no Config parameter is used, but not otherwise
* application.conf should normally not be used by tests,
  but if someone prefer that it's easy enough to use it via
  `ConfigFactory.load()` or via include in application-test.conf
This commit is contained in:
Patrik Nordwall 2019-07-10 11:45:57 +02:00
parent 4561994a59
commit af77bf3fb0
11 changed files with 213 additions and 9 deletions

View file

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

View file

@ -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])))
/**

View file

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

View file

@ -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()))
/**

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 Lightbend Inc. <https://www.lightbend.com>
*/
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
;
}
}

View file

@ -0,0 +1,2 @@
# used by ActorTestKitSpec
test.from-application-test = yes

View file

@ -0,0 +1,2 @@
# used by ActorTestKitSpec
test.from-application = yes

View file

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

View file

@ -0,0 +1,31 @@
/*
* Copyright (C) 2019 Lightbend Inc. <https://www.lightbend.com>
*/
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
}
}

View file

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

View file

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