繁体   English   中英

Scala在从application.conf获取数据源时没有启动应用程序

[英]Scala Play no application started when grabbing data sources from application.conf

我试图从我的application.conf文件中读取数据源,但每次运行我的服务器,或者尝试运行测试用例时,我都会收到错误消息,说明没有应用程序启动。

这是我想要做的一个例子:

尝试从我的application.conf读取属性的单元测试

class DbConfigWebUnitTest extends PlaySpec with OneAppPerSuite {

  implicit override lazy val app: FakeApplication = FakeApplication(
    additionalConfiguration = Map("db.test.url" -> "jdbc:postgresql://localhost:5432/suredbitswebtest",
      "db.test.user" -> "postgres", "db.test.password" -> "postgres", "db.test.driver" -> "org.postgresql.Driver"))
  val dbManagementWeb = new DbManagementWeb with DbConfigWeb with DbTestQualifier
  "DbConfigWebTest" must {
    "have the same username as what is defined in application.conf" in {
      dbManagementWeb.username must be("postgres")
    }
  }
}

这是我的DbConfigWeb

import play.api.Play.current    
trait DbConfigWeb extends DbConfig { qualifier: DbQualifier =>

      val url: String = current.configuration.getString(qualifier + ".url").get
      val username: String = current.configuration.getString(qualifier + ".user").get
      val password: String = current.configuration.getString(qualifier + ".password").get
      val driver: String = current.configuration.getString(qualifier + ".driver").get
      override def database: DatabaseDef = JdbcBackend.Database.forURL(url, username, password, null, driver)
      override implicit val session = database createSession
    }

    trait DbQualifier {
      val qualifier: String
    }

    trait DbProductionQualifier extends DbQualifier {
      override val qualifier = "db.production"
    }

    trait DbTestQualifier extends DbQualifier {
      override val qualifier = "db.test"
    }

最后这是我的堆栈跟踪:

[suredbits-web] $ last test:test
[debug] Forking tests - parallelism = false
[debug] Create a single-thread test executor
[debug] Runner for sbt.FrameworkWrapper produced 0 initial tasks for 0 tests.
[debug] Runner for org.scalatest.tools.Framework produced 2 initial tasks for 2 tests.
[debug]   Running TaskDef(com.suredbits.web.db.DbConfigWebUnitTest, sbt.ForkMain$SubclassFingerscan@48687c55, false, [SuiteSelector])
[error] Uncaught exception when running com.suredbits.web.db.DbConfigWebUnitTest: java.lang.RuntimeException: There is no started application
sbt.ForkMain$ForkError: There is no started application
    at scala.sys.package$.error(package.scala:27)
    at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
    at play.api.Play$$anonfun$current$1.apply(Play.scala:71)
    at scala.Option.getOrElse(Option.scala:120)
    at play.api.Play$.current(Play.scala:71)
    at com.suredbits.web.db.DbConfigWeb$class.$init$(DbConfigWebProduction.scala:14)
    at com.suredbits.web.db.DbConfigWebUnitTest$$anon$1.<init>(DbConfigWebUnitTest.scala:14)
    at com.suredbits.web.db.DbConfigWebUnitTest.<init>(DbConfigWebUnitTest.scala:14)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at java.lang.Class.newInstance(Class.java:379)
    at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:641)
    at sbt.ForkMain$Run$2.call(ForkMain.java:294)
    at sbt.ForkMain$Run$2.call(ForkMain.java:284)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

我认为关键问题是Scala特征中的val是在构建时初始化的,这是在测试Play应用程序启动之前(可能是它的生命周期与每个规范示例相关联。)您有几个解决方法:

  • DbConfigWeb的所有DbConfigWeb成为def或者lazy val
  • DbConfigWeb一个抽象的play.api.Application字段,从中提取配置值(而不是使用current值)并将其显式传递给虚拟应用程序(假应用程序)到任何DbManagementWeb作为构造函数参数

这是一个简化版本,使用第一种方法(适用于我):

import play.api.Play.current

trait DbConfig

trait DbConfigWeb extends DbConfig {
  self: DbQualifier =>

  // Using defs instead of vals
  def url: String = current.configuration.getString(qualifier + ".url").get
  def username: String = current.configuration.getString(qualifier + ".user").get
  def password: String = current.configuration.getString(qualifier + ".password").get
  def driver: String = current.configuration.getString(qualifier + ".driver").get
}

trait DbQualifier {
  val qualifier: String
}

trait DbTestQualifier extends DbQualifier {
  override val qualifier = "db.test"
}

和规格:

import controllers.{DbConfigWeb, DbTestQualifier}
import org.scalatestplus.play.{OneAppPerSuite, PlaySpec}
import play.api.test.FakeApplication

class DbConfigTest extends PlaySpec with OneAppPerSuite {
  implicit override lazy val app: FakeApplication = FakeApplication(
    additionalConfiguration = Map("db.test.url" -> "jdbc:h2:mem:play",
      "db.test.user" -> "sa", "db.test.password" -> "", "db.test.driver" -> "org.h2.Driver"))
  val dbManagementWeb = new DbConfigWeb with DbTestQualifier
  "DbConfigWebTest" must {
    "have the same username as what is defined in application.conf" in {
      dbManagementWeb.username must be("sa")
    }
  }
}

我个人更喜欢第二种方法,它保持应用程序状态明确传递而不是依赖于play.api.Play.current ,你不能依赖它总是被启动。

你在评论中提到lazy val不能为你工作,但我只能猜测某些调用链是强制初始化的:再次检查不是这种情况。

另请注意, val s的初始化顺序可能很复杂,虽然有些人可能不同意,但坚持将def作为特征成员是非常安全的选择,除非你确定它是一些昂贵的操作(在这种情况下,懒惰的val可能是一个选项。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM