【问题标题】:How to run ScalaTest with Guice DI and Slick?如何使用 Guice DI 和 Slick 运行 ScalaTest?
【发布时间】:2015-10-23 12:35:44
【问题描述】:

我不知道如何配置GuiceApplicationBuilder,以便我能够加载需要注入DatabaseConfigProvider 的控制器。

我想指定一个替代的 postgres 数据库进行测试,或者一个内存数据库(如果可能的话)。

代码

class   User
extends MySpecs
with    OneAppPerTest
{
    override def newAppForTest( testData: TestData ) = new GuiceApplicationBuilder()
        // Somehow bind a database here, I guess?
        .build()

    "A test" should "test" in
    {
        val result = Application.instanceCache[api.controller.User]
            .apply( app )
            .list()( FakeRequest() )

        ...
    }
}

堆栈跟踪

[info] - should return an entity *** FAILED ***
[info]   com.google.inject.ConfigurationException: Guice configuration errors:
[info] 
[info] 1) No implementation for play.api.db.slick.DatabaseConfigProvider was bound.
[info]   while locating play.api.db.slick.DatabaseConfigProvider
[info]     for parameter 1 at api.controller.User.<init>(User.scala:22)
[info]   while locating api.controller.User
[info] 
[info] 1 error
[info]   at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
[info]   at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
[info]   at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
[info]   at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:321)
[info]   at play.api.inject.guice.GuiceInjector.instanceOf(GuiceInjectorBuilder.scala:316)
[info]   at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info]   at play.api.Application$$anonfun$instanceCache$1.apply(Application.scala:234)
[info]   at play.utils.InlineCache.fresh(InlineCache.scala:69)
[info]   at play.utils.InlineCache.apply(InlineCache.scala:55)
[info]   ...

【问题讨论】:

    标签: scala playframework guice slick play-slick


    【解决方案1】:

    你需要给你的GuiceApplicationBuilder()添加一个配置,然后一切都应该由play框架自动处理。这样的事情应该会有所帮助:

    val app = new GuiceApplicationBuilder()
            .configure(
                Configuration.from(
                    Map(
                        "slick.dbs.YOURDBNAME.driver" -> "slick.driver.H2Driver$",
                        "slick.dbs.YOURDBNAME.db.driver" -> "org.h2.Driver",
                        "slick.dbs.YOURDBNAME.db.url" -> "jdbc:h2:mem:",
    
                        "slick.dbs.default.driver" -> "slick.driver.MySQLDriver$",
                        "slick.dbs.default.db.driver" -> "com.mysql.jdbc.Driver"
                    )
                )
            )
            .in(Mode.Test)
            .build()
    

    【讨论】:

      【解决方案2】:

      这种方法有一些设置,但最终结果是公平的。首先,通过扩展实现您自己的GuiceApplicationLoader。见我的answer how to implement it。为什么要使用自己的应用程序加载器?您可以为每个 Prod/Dev/Test 模式指定不同的配置/模块 - 以及不同的数据源。您的主要application.conf 不会配置数据源。相反,您会将其移动到特定于环境的配置,这些配置无论如何都会被应用程序加载器与主配置合并。您的 dev.conf 如下所示:

      slick.dbs {
        default {
          driver = "slick.driver.PostgresDriver$",
          db {
            driver = "org.postgresql.Driver",
            url = "jdbc:postgresql://localhost:5432/dev",
            user = "postgres"
            password = "postgres"
          }
        }
      }
      

      现在的诀窍是使用相同数据源名称,在本例中为default,用于所有其他配置(数据库网址、驱动程序、凭据等会有所不同)。通过这样的设置,您的 evolutions 将应用于您的测试和开发数据库。您的 test.conf 可能如下所示:

      slick.dbs {
        default {
          // in memory configuration
        }
      }
      

      在您的测试中,将WithApplicationLoader 与您的自定义应用程序加载器一起使用,仅此而已。

      @RunWith(classOf[JUnitRunner])
      class ApplicationSpec extends Specification {
      
          "Application" should {
      
              "return text/html ok for home" in new WithApplicationLoader(new CustomApplicationLoader) {
                val home = route(FakeRequest(routes.ApplicationController.home())).get
                status(home) must equalTo(OK)
                contentType(home) must beSome.which(_ == "text/html")
              }
      
          }
      
      }
      

      在测试本身中,您可以访问app: Application 值:

      val service = app.injector.instanceOf(classOf[IService])
      

      【讨论】:

      • 那么你如何将测试代码与产品代码解耦呢?例如,如果我想在TestModule 中有一些模拟类怎么办?由于您的 CustomApplicationLoader 将在生产代码中,这不是问题吗?
      • CustomApplicationLoader 将在安装了test.confTestModule 的测试中以Test 模式运行。
      • 我知道这一点,但是您在 CustomApplicationLoader 中使用了 new TestModule(),这意味着 TestModule 中使用的所有类都必须可以从生产代码中访问,很多人们(包括我自己)不喜欢混合测试和生产代码。
      • 我通过特征(接口)注入我的依赖项,并且我有一个来自所有三个模块的抽象基础模块,在我绑定共享实现的地方扩展。所以CommonModule 包含共享实现,TestModule 特定于测试和ProdModule 特定于产品。
      • 是的,但是如果 TestModule 包含所有仅测试类,并且您在 CustomApplicationLoader 中执行 new TestModule() (在您的 src 中),那么您还必须放置所有仅测试类由TestModulesrc 中使用,而不是test 文件夹,对吧? testsrc 文件夹之间必须存在耦合,或者我错过了什么?
      猜你喜欢
      • 1970-01-01
      • 2016-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-17
      • 2019-12-18
      • 2013-09-20
      • 2015-11-28
      相关资源
      最近更新 更多