【问题标题】:Testing Play + Slick app测试 Play + Slick 应用程序
【发布时间】:2016-03-03 12:10:38
【问题描述】:

我有一个简单的 CRUD 应用程序,使用 Scala Play 2.4.3 和 Play-slick 1.1.0 (slick 3.1.0) 构建,它使用 MySQL 数据库进行持久存储。

我正在尝试为我的应用创建测试,我看到了 2 个主要选项:

  • 据我所知,模拟数据库访问需要一些代码更改
  • 让测试使用替代数据库(可能在内存 H2 中)。

最好的方法是什么(优势和劣势)?

我更喜欢第二种方法,但我在设置测试时发现了一些困难。

我需要做什么?首先,我认为我需要使用FakeApplication 进行测试,对吧?我需要任何 sbt 依赖才能做到这一点吗?

之后如何指定使用H2数据库?

【问题讨论】:

    标签: scala testing playframework-2.0 slick slick-3.0


    【解决方案1】:

    我有同样的挣扎,我想出了这样的解决方案(使用第二种方法):

    创建一个供 DAO 使用的上下文:

    trait BaseContext{
    
      def dbName: String
    
      val dbConfig = DatabaseConfigProvider.get[JdbcProfile](dbName)
      val db = dbConfig.db
      val profile = dbConfig.driver
      val tables = new Tables {  // this is generated by Schema Code Generator
        override val profile: JdbcProfile = dbConfig.driver
      }
    }
    
    @Singleton
    class AppContext extends BaseContext{
      def dbName = "mysql"  // name in your conf right after "slick.dbs"
    }
    
    @Singleton
    class TestingContext extends BaseContext{
      def dbName = "h2"
    }
    

    然后创建一个模块来绑定注入,别忘了在conf中使用play.modules.enabled += "your.Module"启用它:

    class ContextModule(environment: Environment, configuration: Configuration) extends AbstractModule {
    
      override def configure(): Unit = {
        if (configuration.getString("app.mode").contains("test")) {
          bind(classOf[BaseContext])
              .to(classOf[TestingContext])
        } else {
          bind(classOf[BaseContext])
              .to(classOf[AppContext])
        }
      }
    }
    

    并将其注入您创建的每个 DAO:

    class SomeDAO @Inject()(context: BaseContext){
    
      val dbConfig = context.dbConfig
      val db = context.db
      val tables = context.tables
      import tables.profile.api._
    
      def otherStuff....
      // you can call db.run(...), tables.WhateverYourTableIs, tables.TableRowCaseClass, ...
    }
    

    最后一步,您的配置文件。在我的例子中,我使用app.mode 来标记环境,我使用单独的.conf 来标记不同的环境。当然,在这些配置中,您必须具有正确的数据库配置。这是示例:

    app.mode = "test"
    
    # Database configuration
    slick.dbs = {
      # for unit test
      h2 {
        driver = "slick.driver.H2Driver$"
        db = {
          url = "jdbc:h2:mem:test;MODE=MYSQL"
          driver = "org.h2.Driver"
          keepAliveConnection = true
        }
      }
    }
    

    我很确定我的解决方案不是一个优雅的解决方案,但它可以交付货物。 :) 欢迎任何更好的解决方案!

    【讨论】:

    • 原来 Typesafe 有一个项目可以证明这一点。 github.com/typesafehub/activator-slick-multidb
    • 我完全了解这个例子,它确实给了我一些提示。但是,这并没有 100% 达到我的预期。
    • 你错过了什么?在最新的分支 3.1 中,它们具有您在上面显示的内容以及一些内容。
    • 以那个演示项目为例,它演示了如何在运行时流畅地访问 2 个数据库,而我的主要目标是在测试中用相同的业务逻辑(一组代码)用 h2 替换 mysql。这意味着在为实际功能或单元测试编码时,我不必关心数据库。这使得 conf 文件成为最好的地方,所以我编写了自己的方法。在那之后,我完全满足并且没有跟踪最新的浮油。如果有更好的方法,请赐教。真的困扰了我很久。
    • 我自己解决了这个问题。他们似乎没有提供一个很好的方法来做到这一点。我在测试中启动了一个内存 H2 并有 2 个 DAO,一个使用 Slick ORM,另一个使用普通 SQL。有各种设​​置似乎会随机影响测试,例如是否启用连接池、PatienceConfig(来自org.scalatest.concurrent)。我在每次测试之前和之后创建和删除一个测试用户,有时用户在测试完成之前就被删除了。如果我在事务中运行,结果会有所不同。 MongoDB 不会发生这种情况,只有 RDBMS(内存中?)
    【解决方案2】:

    我的解决方案是在每个规范的开头添加step(Play.start(fakeApp)),在每个规范的末尾添加step(Play.stop(fakeApp))。

    还有:

    def fakeApp: FakeApplication = {
    FakeApplication(additionalConfiguration =
      Map(
        "slick.dbs.default.driver" -> "slick.driver.H2Driver$",
        "slick.dbs.default.db.driver" -> "org.h2.Driver",
        "slick.dbs.default.db.url" -> "jdbc:h2:mem:play"
      ))
    

    }

    这是需要的,因为我使用的是play-slick,它需要如下配置:

    slick.dbs.default.driver = "slick.driver.MySQLDriver$"
    slick.dbs.default.db.driver = "com.mysql.jdbc.Driver"
    slick.dbs.default.db.url = "jdbc:mysql://localhost/database"
    slick.dbs.default.db.user = "user"
    slick.dbs.default.db.password = "password"
    

    有关docs的更多信息

    【讨论】:

    • 这是什么step?关键字不易搜索。
    • 很可能是 specs2 中的函数,它允许您在测试之前和/或之后运行某些东西。类似于您使用 beforeAll / afterAll 的内容。
    猜你喜欢
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多