【问题标题】:Testing Scala Play + Slick: why is it picking the wrong database?测试 Scala Play + Slick:为什么选择错误的数据库?
【发布时间】:2017-05-14 04:04:00
【问题描述】:

我正在开发使用 Play 2.5.10 和 Slick 3.1.1 的 this github project(可以通过运行 sbt test 重现该问题,但也可以运行 also be checked directly in travis CI)。我使用 Postgres 数据库配置 default 进行开发和生产。然后我使用名为test 的内存数据库中的H2 进行测试。 default 数据库在conf/application.conf 中配置,而test 数据库在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 基类如下所示:

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 数据库。

【问题讨论】:

    标签: scala playframework slick


    【解决方案1】:

    好的,您的问题有点不同(或者可能有点出乎意料)。这是让你头疼的一句话:

    dbApi.databases().foreach(runEvolutions)
    

    它在:ApplicationEvolutions.scala:42

    这可能是不言自明的:)

    问题仍然涉及更多。您的测试环境中实际上有两个数据库(defaulttest)。现在这导致了几个问题 - 您在上面看到了其中一个问题(在每个问题上都尝试了进化)。另一个是,如果你想使用不同命名的数据库,你不能只注入这样的东西:

    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 中进行某种重新映射(然后您将拥有一个用于 prod 的模块和一个用于测试的模块 - 它们可以相互继承并且只覆盖某些东西 - 例如附加 test db代替默认值)。这基本上是一个品味问题。

    【讨论】:

    • 嗨,帕维尔!再次感谢你!好点!但它仍然给我留下了2.sql与H2不兼容的问题。这就是为什么我需要单独的测试数据库以便它有自己的进化文件夹的原因......我已经搜索了 play.evolutions.* 带有前缀的参数来更改进化的根,但没有。此前缀仅存在于测试 API Evolutions.withEvolutions(db, ClassLoaderEvolutionsReader.forPrefix("conf/test/")) 中,但触发得太晚或根本不触发。
    • 哦...对。没有注意到由于 DB 的差异,您有两种不同的进化路径。正确的。在那种情况下,上面可能不起作用。我相信你问的关于禁用进化的另一个问题会解决这个问题。请记住,尽管从长远来看,拥有两种不同的进化路径(测试 + 产品)可能会很麻烦......(但这可能会权衡在 postgres 与嵌入 H2 上运行测试的优缺点)。
    • 嗨,Pawel,谢谢,非常正确!我也决定使用 Postgres 进行测试。原因有两个:最好在数据库中测试最终将部署应用程序,并且使用 Postgres 更容易解决数据库问题,即在 H2 中连接任何客户端并针对它运行选择是一个很大的问题与 Squirrel 连接......啊,还有我选择 Postgres 的另一个重要原因,Travis CI 可以自动部署和运行 postgres 实例以进行单元测试,我在 github 中完成了所有工作。
    猜你喜欢
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-03
    • 2011-03-20
    • 1970-01-01
    • 2019-10-20
    相关资源
    最近更新 更多