3

I am trying to run specification test in Play/Scala/ReactiveMongo project. Setup is like this:

class FeaturesSpec extends Specification {

  "Features controller" should {
    "create feature from JSON request" in withMongoDb { app =>
      // do test
    }
}

With MongoDbFixture as follows:

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

When test runs, logs are pretty noisy, complain about resource being already closed. Although tests work correctly, this is weird and I would like to know why this occurs and how to fix it.

Error:

[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$1.apply(Akka.scala:29) ~[play_2.11-2.3.7.jar:2.3.7]
    at play.api.libs.concurrent.Akka$$anonfun$system$1.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]
Jarek Rozanski
  • 780
  • 1
  • 6
  • 13

2 Answers2

1

The exception means that you are using the ReactiveMongo plugin after the application has stopped.

You might wanna try using 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 {
    ...
  }
}

You should also take a look at Flapdoodle Embedded Mongo, with that you don't have to delete databases after testing IIRC.

Marius Soutier
  • 11,184
  • 1
  • 38
  • 48
0

This problem likely occurs because your test exercises code that references a closed MongoDB instance. After each Play Specs2 test runs, the MongoDb connection is reset, thus your first test may pass, but a subsequent test may hold a stale reference to the closed instance, and as a result fail.

One way to solve this issue is to ensure the following criteria are met in your application:

  • Avoid using val or lazy val for MongoDb database resources
  • (Re)Initialize all database references on application start.

I wrote up a blog post that describes a solution to the problem within the context of a Play Controller.

cmd
  • 11,622
  • 7
  • 51
  • 61