JulienD JulienD - 2 months ago 23
Scala Question

Scala play: H2 "HikariDataSource has been closed."

I am trying to run my tests in an in-memory database with this code:

class MySpec extends PlaySpec with OneAppPerSuite {

Databases.withInMemory(
name = "test",
urlOptions = Map(
"MODE" -> "MYSQL",
"DATABASE_TO_UPPER" -> "false",
"DB_CLOSE_DELAY" -> "-1"
),
config = Map(
"logStatements" -> true,
"lazyInit" -> true,
"username" -> "sa",
"password" -> ""
)
) { TestDb =>

"DataManagementController" should {"connect" in {
TestDb.withConnection( conn => 0 ) // do nothing, but connect
}}

}
}


but I am getting this error: "HikariDataSource has been closed" before the test is executed:

[info] DataManagementController
[info] - should connect *** FAILED ***
[info] java.sql.SQLException: HikariDataSource HikariDataSource (HikariPool-1) has been closed.
[info] at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:79)
[info] at play.api.db.DefaultDatabase.getConnection(Databases.scala:142)
[info] at play.api.db.DefaultDatabase.withConnection(Databases.scala:152)
[info] at play.api.db.DefaultDatabase.withConnection(Databases.scala:148)
[info] at ControllerSpec$$anonfun$1$$anonfun$apply$1$$anonfun$apply$mcV$sp$1.apply$mcV$sp(ControllerSpec.scala:37)
[info] at ControllerSpec$$anonfun$1$$anonfun$apply$1$$anonfun$apply$mcV$sp$1.apply(ControllerSpec.scala:37)
[info] at ControllerSpec$$anonfun$1$$anonfun$apply$1$$anonfun$apply$mcV$sp$1.apply(ControllerSpec.scala:37)
[info] at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
[info] at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
[info] at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info] ...


What am I doing wrong?

Answer

When you use OneAppPerSuite, Play will start an app instance for each suite. In your case, you are creating a test database before the app is ready. So you have to invert the order of your code to something like this:

"DataManagementController" should {
    "connect" in {
        Databases.withInMemory(
          // Options and config here
        ) { TestDb =>
            TestDb.withConnection( conn => 0 )
        }
    }
}

You can also encapsulate the db initialization part to make it easier to use in each test that needs that db:

def testDB[T](block: Database => T) = {
    Databases.withInMemory(
      name = "test",
      urlOptions = Map(
        "MODE" -> "MYSQL",
        "DATABASE_TO_UPPER" -> "false",
        "DB_CLOSE_DELAY" -> "-1"
      ),
      config = Map(
        "logStatements" -> true,
        "lazyInit" -> true,
        "username" -> "sa",
        "password" -> ""
      )
    ) { block }
  }

and then use it like this:

"DataManagementController" should {
    "connect" in {
       testDB { db => db.withConnection(conn => 0) }
    }
}

Source: https://www.playframework.com/documentation/2.5.x/ScalaTestingWithDatabases

Comments