Play 报告关闭后无法获取 ClosableLazy 值
Play reports that it can't get ClosableLazy value after it has been closed
我正尝试在 Play/Scala/ReactiveMongo 项目中进行 运行 规范测试。设置是这样的:
class FeaturesSpec extends Specification {
"Features controller" should {
"create feature from JSON request" in withMongoDb { app =>
// do test
}
}
使用MongoDbFixture如下:
object MongoDBTestUtils {
def withMongoDb[T](block: Application => T): T = {
implicit val app = FakeApplication(
additionalConfiguration = Map("mongodb.uri" -> "mongodb://localhost/unittests")
)
running(app) {
def db = ReactiveMongoPlugin.db
try {
block(app)
} finally {
dropAll(db)
}
}
}
def dropAll(db: DefaultDB) = {
Await.ready(Future.sequence(Seq(
db.collection[JSONCollection]("features").drop()
)), 2 seconds)
}
}
测试 运行s 时,日志非常嘈杂,抱怨资源已经关闭。虽然测试工作正常,但这很奇怪,我想知道为什么会发生这种情况以及如何解决它。
错误:
[info] application - ReactiveMongoPlugin stops, closing connections...
[warn] play - Error stopping plugin
java.lang.IllegalStateException: Can't get ClosableLazy value after it has been closed
at play.core.ClosableLazy.get(ClosableLazy.scala:49) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.AkkaPlugin.applicationSystem(Akka.scala:71) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.Akka$$anonfun$system.apply(Akka.scala:29) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.Akka$$anonfun$system.apply(Akka.scala:29) ~[play_2.11-2.3.7.jar:2.3.7]
at scala.Option.map(Option.scala:145) [scala-library-2.11.4.jar:na]
异常说明您在应用停止后使用ReactiveMongo插件。
您可能想尝试使用 Around
:
class withMongoDb extends Around with Scope {
val db = ReactiveMongoPlugin.db
override def around[T: AsResult](t: => T): Result = try {
val res = t
AsResult.effectively(res)
} finally {
...
}
}
您还应该看看 Flapdoodle Embedded Mongo,这样您就不必在测试 IIRC 后删除数据库。
出现此问题的原因可能是您的测试练习代码引用了已关闭的 MongoDB 实例。每次运行 Play Specs2 测试后,MongoDb 连接都会重置,因此您的第一个测试可能会通过,但后续测试可能会持有对已关闭实例的陈旧引用,因此会失败。
解决此问题的一种方法是确保您的应用程序满足以下条件:
- 避免对 MongoDb 数据库资源使用 val 或 lazy val
- (重新)在应用程序启动时初始化所有数据库引用。
我写了一个 blog post 描述了在 Play Controller 的上下文中解决问题的方法。
我正尝试在 Play/Scala/ReactiveMongo 项目中进行 运行 规范测试。设置是这样的:
class FeaturesSpec extends Specification {
"Features controller" should {
"create feature from JSON request" in withMongoDb { app =>
// do test
}
}
使用MongoDbFixture如下:
object MongoDBTestUtils {
def withMongoDb[T](block: Application => T): T = {
implicit val app = FakeApplication(
additionalConfiguration = Map("mongodb.uri" -> "mongodb://localhost/unittests")
)
running(app) {
def db = ReactiveMongoPlugin.db
try {
block(app)
} finally {
dropAll(db)
}
}
}
def dropAll(db: DefaultDB) = {
Await.ready(Future.sequence(Seq(
db.collection[JSONCollection]("features").drop()
)), 2 seconds)
}
}
测试 运行s 时,日志非常嘈杂,抱怨资源已经关闭。虽然测试工作正常,但这很奇怪,我想知道为什么会发生这种情况以及如何解决它。
错误:
[info] application - ReactiveMongoPlugin stops, closing connections...
[warn] play - Error stopping plugin
java.lang.IllegalStateException: Can't get ClosableLazy value after it has been closed
at play.core.ClosableLazy.get(ClosableLazy.scala:49) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.AkkaPlugin.applicationSystem(Akka.scala:71) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.Akka$$anonfun$system.apply(Akka.scala:29) ~[play_2.11-2.3.7.jar:2.3.7]
at play.api.libs.concurrent.Akka$$anonfun$system.apply(Akka.scala:29) ~[play_2.11-2.3.7.jar:2.3.7]
at scala.Option.map(Option.scala:145) [scala-library-2.11.4.jar:na]
异常说明您在应用停止后使用ReactiveMongo插件。
您可能想尝试使用 Around
:
class withMongoDb extends Around with Scope {
val db = ReactiveMongoPlugin.db
override def around[T: AsResult](t: => T): Result = try {
val res = t
AsResult.effectively(res)
} finally {
...
}
}
您还应该看看 Flapdoodle Embedded Mongo,这样您就不必在测试 IIRC 后删除数据库。
出现此问题的原因可能是您的测试练习代码引用了已关闭的 MongoDB 实例。每次运行 Play Specs2 测试后,MongoDb 连接都会重置,因此您的第一个测试可能会通过,但后续测试可能会持有对已关闭实例的陈旧引用,因此会失败。
解决此问题的一种方法是确保您的应用程序满足以下条件:
- 避免对 MongoDb 数据库资源使用 val 或 lazy val
- (重新)在应用程序启动时初始化所有数据库引用。
我写了一个 blog post 描述了在 Play Controller 的上下文中解决问题的方法。