测试 Scala Play + Slick:为什么它选择了错误的数据库?
Testing Scala Play + Slick: why is it picking the wrong database?
我正在研究 this github project that uses Play 2.5.10 and Slick 3.1.1 (the issue can be reproduced there by running sbt test
but can also be checked directly in travis CI). I use a Postgres database configuration default
for development and production. I then use a H2 in memory database called test
for testing. The default
database is configured in conf/application.conf
whereas the test
database is configured in conf/application.test.conf
。
问题是,为了测试,我用名称 test
初始化了数据库,但是用 GuiceApplicationBuilder
构建的应用程序仍然选择 default
那个。
这一行在我的build.sbt
中拿起测试配置:
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
这是该文件的内容:
include "application.conf"
slick.dbs {
test {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
我的 DaoFunSpec
基地 class 看起来像这样:
package dao
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import org.scalatestplus.play.OneAppPerSuite
import play.api.Application
import play.api.db.evolutions.Evolutions
import play.api.db.DBApi
abstract class DaoFunSpec extends FunSpec with OneAppPerSuite with BeforeAndAfterAll {
lazy implicit val db = app.injector.instanceOf[DBApi].database("test")
override def beforeAll() {
Evolutions.applyEvolutions(db)
}
override def afterAll() {
Evolutions.cleanupEvolutions(db)
}
def userDao(implicit app: Application) = {
Application.instanceCache[UserDao].apply(app)
}
}
请注意 app.injector.instanceOf[DBApi].database("test")
行,但 Play 仍会尝试连接到 default
数据库。
好的,您的问题有点不同(或者可能有点出乎意料)。这是让您头疼的一行:
dbApi.databases().foreach(runEvolutions)
它位于:ApplicationEvolutions.scala:42
这可能是不言自明的:)
问题还是比较复杂。您的测试环境中实际上有两个数据库(default
和 test
)。现在这导致了几个问题——其中一个你在上面看到了(对每个问题都尝试了进化)。另一个是,如果你想使用不同命名的数据库,你不能只注入类似的东西:
class UserDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
相反,您需要使用 (AFAIR):
class UserDao @Inject()(@NamedDatabase("test") protected val dbConfigProvider: DatabaseConfigProvider)
但在那种情况下你的测试变得更加复杂。
如果:
一切都会简单得多
1) 您可以将通用配置提取到 common.conf
2) 你会把你的 application.conf
改成这样:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.PostgresDriver$"
db.driver="org.postgresql.Driver"
db.url="jdbc:postgresql://localhost:5432/exampledb?searchpath=public"
db.user="postgres"
db.password="postgres"
}
}
3) 您可以将 application.test.conf
更改为如下所示:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
现在唯一的事情是你应该有一组演变(default
)这实际上并没有那么糟糕,因为它可以确保你的测试数据库与你的生产数据库同步(至少在结构方面)。
并非以上是唯一的解决方案。您仍然可以有两个不同名称的数据库配置;在这种情况下,您需要在 Guilce module
中进行某种重新映射(然后您将拥有一个用于生产的模块和一个用于测试的模块 - 它们可以相互继承并且只覆盖某些东西 - 例如附加 test
db 代替默认值)。这基本上是一个品味问题。
我正在研究 this github project that uses Play 2.5.10 and Slick 3.1.1 (the issue can be reproduced there by running sbt test
but can also be checked directly in travis CI). I use a Postgres database configuration default
for development and production. I then use a H2 in memory database called test
for testing. The default
database is configured in conf/application.conf
whereas the test
database is configured in conf/application.test.conf
。
问题是,为了测试,我用名称 test
初始化了数据库,但是用 GuiceApplicationBuilder
构建的应用程序仍然选择 default
那个。
这一行在我的build.sbt
中拿起测试配置:
javaOptions in Test += "-Dconfig.file=conf/application.test.conf"
这是该文件的内容:
include "application.conf"
slick.dbs {
test {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
我的 DaoFunSpec
基地 class 看起来像这样:
package dao
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import org.scalatestplus.play.OneAppPerSuite
import play.api.Application
import play.api.db.evolutions.Evolutions
import play.api.db.DBApi
abstract class DaoFunSpec extends FunSpec with OneAppPerSuite with BeforeAndAfterAll {
lazy implicit val db = app.injector.instanceOf[DBApi].database("test")
override def beforeAll() {
Evolutions.applyEvolutions(db)
}
override def afterAll() {
Evolutions.cleanupEvolutions(db)
}
def userDao(implicit app: Application) = {
Application.instanceCache[UserDao].apply(app)
}
}
请注意 app.injector.instanceOf[DBApi].database("test")
行,但 Play 仍会尝试连接到 default
数据库。
好的,您的问题有点不同(或者可能有点出乎意料)。这是让您头疼的一行:
dbApi.databases().foreach(runEvolutions)
它位于:ApplicationEvolutions.scala:42
这可能是不言自明的:)
问题还是比较复杂。您的测试环境中实际上有两个数据库(default
和 test
)。现在这导致了几个问题——其中一个你在上面看到了(对每个问题都尝试了进化)。另一个是,如果你想使用不同命名的数据库,你不能只注入类似的东西:
class UserDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
相反,您需要使用 (AFAIR):
class UserDao @Inject()(@NamedDatabase("test") protected val dbConfigProvider: DatabaseConfigProvider)
但在那种情况下你的测试变得更加复杂。
如果:
一切都会简单得多1) 您可以将通用配置提取到 common.conf
2) 你会把你的 application.conf
改成这样:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.PostgresDriver$"
db.driver="org.postgresql.Driver"
db.url="jdbc:postgresql://localhost:5432/exampledb?searchpath=public"
db.user="postgres"
db.password="postgres"
}
}
3) 您可以将 application.test.conf
更改为如下所示:
include "common.conf"
slick.dbs {
default {
driver="slick.driver.H2Driver$"
db.driver="org.h2.Driver"
db.url="jdbc:h2:mem:test;MODE=PostgreSQL"
db.username="sa"
db.password=""
}
}
现在唯一的事情是你应该有一组演变(default
)这实际上并没有那么糟糕,因为它可以确保你的测试数据库与你的生产数据库同步(至少在结构方面)。
并非以上是唯一的解决方案。您仍然可以有两个不同名称的数据库配置;在这种情况下,您需要在 Guilce module
中进行某种重新映射(然后您将拥有一个用于生产的模块和一个用于测试的模块 - 它们可以相互继承并且只覆盖某些东西 - 例如附加 test
db 代替默认值)。这基本上是一个品味问题。