GuiceApplicationBuilder -> evolution пытается получить доступ к prod db при запуске теста

Мы используем

  • Scala 2.12.5
  • играть 2.6.14 с эволюцией
  • пятно 3.2.3
  • в многомодульном проекте в IntelliJ 2018.1

В подпроекте "сервис" мы (хотим) использовать Guice для переключения между MySql для prod и H2 для модульного тестирования. Это работало в прошлом, но теперь мы используем также эволюции, которые создают проблемы.

Наш файл application.conf содержит несколько строк для эволюции:

db.default = {
  driver="com.mysql.jdbc.Driver"
  url="jdbc:mysql://localhost:3306/dev"
  url=${?rds.url}
  username="dev-user"
  username= ${?rds.user}
  password="<secret>"
  password=${?rds.password}
}

Один пример теста класса выглядит

class HomeControllerTest extends PlaySpec with GuiceOneAppPerTest with BeforeAndAfterAll {

  val application: play.api.Application = new GuiceApplicationBuilder()
    .in(Mode.Test)
    .overrides(
      bind[DbComponent].to(classOf[H2DbComponent])
    ).build()

  // the same for:
  override def fakeApplication(): Application = new GuiceApplicationBuilder()...

  // the same for:
  implicit override lazy val app: play.api.Application = new GuiceApplicationBuilder()...

Проблема: при запуске тестовых классов, которые используют Guice, Evolution пытается получить доступ к базе данных MySql по умолчанию. Это не предназначено и провалит тесты при работе на Jenkins. Когда строки 'db.default = {...}' закомментированы, тест выполняется нормально.

Чтобы преодолеть эту проблему, у нас есть альтернативный файл conf/application.test.conf, содержащий строки для переопределения настроек базы данных "по умолчанию":

db.default = {
  driver = "org.h2.Driver"
  url = "jdbc:h2:mem:h2test;MODE=MySql;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS dev_db"
}

и добавил в service/build.sbt строку

javaOptions in Test += "-Dconfig.resource=application.test.conf"

оказывается, что альтернативный конфигурационный файл application.test.conf не используется Guice. Это можно проверить, проанализировав значение 'configuration' в play.api.db.DBApiProvider:: get.

Чтобы преодолеть эту проблему, тестовый класс теперь выглядит так

class HomeControllerTest extends PlaySpec with GuiceOneAppPerTest with BeforeAndAfterAll {

  val configuration: Configuration = Configuration.reference ++ Configuration(ConfigFactory.parseResources("application.test.conf").resolve)

  val application: play.api.Application = new GuiceApplicationBuilder()
    .in(Mode.Test)
    .loadConfig(configuration)
    .overrides(
      bind[DbComponent].to(classOf[H2DbComponent])
    ).build()

Это приводит к ошибке:

Exception encountered when invoking run on a nested suite - Guice configuration errors:

1) No implementation for play.api.Application was bound.
  while locating play.api.Application

1 error
com.google.inject.ConfigurationException: Guice configuration errors:

1) No implementation for play.api.Application was bound.
  while locating play.api.Application

1 error
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1045)
    at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1004)
    at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1054)
    at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:409)
    at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:404)
    at play.api.inject.ContextClassLoaderInjector.$anonfun$instanceOf$2(Injector.scala:117)
    at play.api.inject.ContextClassLoaderInjector.withContext(Injector.scala:126)
    at play.api.inject.ContextClassLoaderInjector.instanceOf(Injector.scala:117)
    at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:137)
    at controllers.HomeControllerTest.fakeApplication(HomeControllerTest.scala:36)
    at org.scalatestplus.play.BaseOneAppPerSuite.app(BaseOneAppPerSuite.scala:29)
    at org.scalatestplus.play.BaseOneAppPerSuite.app$(BaseOneAppPerSuite.scala:29)
    at controllers.HomeControllerTest.app$lzycompute(HomeControllerTest.scala:22)
    at controllers.HomeControllerTest.app(HomeControllerTest.scala:22)
    at org.scalatestplus.play.BaseOneAppPerSuite.run(BaseOneAppPerSuite.scala:42)
    at org.scalatestplus.play.BaseOneAppPerSuite.run$(BaseOneAppPerSuite.scala:41)
    at controllers.HomeControllerTest.org$scalatest$BeforeAndAfterAll$$super$run(HomeControllerTest.scala:22)
    at org.scalatest.BeforeAndAfterAll.liftedTree1$1(BeforeAndAfterAll.scala:213)
    at org.scalatest.BeforeAndAfterAll.run(BeforeAndAfterAll.scala:210)
    at org.scalatest.BeforeAndAfterAll.run$(BeforeAndAfterAll.scala:208)
    at controllers.HomeControllerTest.run(HomeControllerTest.scala:22)
    at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:45)
    at org.scalatest.tools.Runner$.$anonfun$doRunRunRunDaDoRunRun$13(Runner.scala:1346)
    at org.scalatest.tools.Runner$.$anonfun$doRunRunRunDaDoRunRun$13$adapted(Runner.scala:1340)
    at scala.collection.immutable.List.foreach(List.scala:389)
    at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:1340)
    at org.scalatest.tools.Runner$.$anonfun$runOptionallyWithPassFailReporter$24(Runner.scala:1031)
    at org.scalatest.tools.Runner$.$anonfun$runOptionallyWithPassFailReporter$24$adapted(Runner.scala:1010)
    at org.scalatest.tools.Runner$.withClassLoaderAndDispatchReporter(Runner.scala:1506)
    at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter(Runner.scala:1010)
    at org.scalatest.tools.Runner$.run(Runner.scala:850)
    at org.scalatest.tools.Runner.run(Runner.scala)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.runScalaTest2(ScalaTestRunner.java:131)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:28)

В качестве обходного пути к проблеме я изменил все значения по умолчанию для данных подключения для базы данных по умолчанию в application.conf на значения, ожидаемые H2:

// DB connect information - needed for Play Evolutions
db.default = {
  driver="org.h2.Driver"   // value needed for testing on Jenkins (with H2)
  driver=${?rds.driver}    // for dev / stage / prod ->"com.mysql.jdbc.Driver"

  url="jdbc:h2:mem:h2test;MODE=MySql;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS dev_db" // for testing on Jenkins (H2)
  url=${?rds.url}         // for dev / stage / prod -> "jdbc:mysql://localhost:3306/<schema-name>"

  username=""              // value needed for testing on Jenkins (with H2)
  username=${?rds.user}
  password=""              // value needed for testing on Jenkins (with H2)
  password=${?rds.password}
}

play.evolutions.enabled=false  // value needed for testing on Jenkins (with H2) - evolutions must be disabled
play.evolutions.enabled=${?VALUE}  // set to "true" for 'dev' and 'stage'

Но каково чистое, предлагаемое и работающее решение для эволюции тестовых баз данных в памяти?

Спасибо заранее

0 ответов

Другие вопросы по тегам