10

我有以下使用 PlaySlick 连接到数据库的 DAO。该类有一个read我需要用 ScalaTest 测试的方法。我的问题是我不知道如何模拟DatabaseConfigProviderUsersDAO类中注入它并测试read方法。这是要测试的类:

class UsersDAO @Inject()(@NamedDatabase("mydb") 
          protected val dbConfigProvider: DatabaseConfigProvider)
          extends with HasDatabaseConfigProvider[JdbcProfile] {

   import driver.api._

   val db1 = dbConfigProvider.get[JdbcProfile].db

   def read (sk: Int) = {
      val users = TableQuery[UserDB]
      val action = users.filter(_.sk === sk).result
      val future = db1.run(action.asTry)
      future.map{
        case Success(s) => 
          if (s.length>0)
            Some(s(0))
          else
            None
        case Failure(e) => throw new Exception ("Failure: " + e.getMessage)
      }
   }

}

这是我编写测试的尝试:

class UserDAOTest extends PlaySpec with OneAppPerSuite  {

  implicit override lazy val app = new GuiceApplicationBuilder().
  configure(
            Configuration.from(
                Map(
                    "slick.dbs.mydb.driver" -> "slick.driver.MySQLDriver$",
                    "slick.dbs.mydb.db.driver" -> "com.mysql.jdbc.Driver",
                    "slick.dbs.mydb.db.url" -> "jdbc:mysql://localhost:3306/control",
                    "slick.dbs.mydb.db.user" -> "root",
                    "slick.dbs.mydb.db.password" -> "xxxxx"
                )
            )
        ).build

  val dbConfigProvider = app.injector.instanceOf[DatabaseConfigProvider]

  "Example " should {
    "be valid" in {

      val controller = new UsersDAO(dbConfigProvider)
      val result = controller.read(1)
      println(result) 
    }
  }

当我运行测试时,它失败并显示以下错误消息:

com.google.inject.ConfigurationException:Guice 配置错误:

1) 没有绑定 play.api.db.slick.DatabaseConfigProvider 的实现。同时定位 play.api.db.slick.DatabaseConfigProvider

4

2 回答 2

8

恕我直言,最好不要干扰 Play 的注入内容,而只是使用它。这应该有效:

class UserDAOTest extends PlaySpec with OneAppPerSuite with ScalaFutures {

  implicit override lazy val app = new GuiceApplicationBuilder().
    configure(
      "slick.dbs.mydb.driver" -> "slick.driver.MySQLDriver$",
      "slick.dbs.mydb.db.driver" -> "com.mysql.jdbc.Driver",
      "slick.dbs.mydb.db.url" -> "jdbc:mysql://localhost:3306/control",
      "slick.dbs.mydb.db.user" -> "root",
      "slick.dbs.mydb.db.password" -> "xxxxx").build

  def userDAO(implicit app: Application): UserDAO = Application.instanceCache[UserDAO].apply(app)

  "UserDAO" should {
    "do whatever" in {
      whenReady(userDAO.read(1)) { res =>
        println(res)
      }
    }
  }

}

我已经更新了我的回购,以防我错过了什么。

于 2017-01-09T22:42:03.307 回答
3

我不确定您是否想完全从测试中模拟数据库,或者只是使用不同的数据库配置。

查看您的代码示例,我假设后者。

如果您真的想在开始时这样做,最简单的解决方案是:

// instead of your line below
//  val dbConfigProvider = app.injector.instanceOf[DatabaseConfigProvider]
// use this:
   val userDao = app.injector.instanceOf[UsersDao]

以上将注入您的 DAO 并隐式处理您的DatabaseConfigProvider.

但是我在这里不明白一件事-您为什么不在application.conf测试资源中创建另一个配置(),其内容如下:

slick.dbs.mydb.driver="slick.driver.MySQLDriver$"
slick.dbs.mydb.db.driver="com.mysql.jdbc.Driver"
slick.dbs.mydb.db.url = "jdbc:mysql://localhost:3306/control"
slick.dbs.mydb.db.user=root
slick.dbs.mydb.db.password="xxxxx"

完成此操作后,只需将您的app创建更改为:

implicit override lazy val app = new GuiceApplicationBuilder().build

并且通常UsersDao像这样注入(与上述相同):

val usersDao = app.injector.instanceOf[UsersDao]

?

(我在上面假设您只是在测试和应用程序中具有不同的数据库配置)。

完整代码

测试/资源/application.conf

slick.dbs.mydb.driver="slick.driver.MySQLDriver$"
slick.dbs.mydb.db.driver="com.mysql.jdbc.Driver"
slick.dbs.mydb.db.url = "jdbc:mysql://localhost:3306/control"
slick.dbs.mydb.db.user=root
slick.dbs.mydb.db.password="xxxxx"

UserDaoTest.scala

import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
//... other imports

class UserDAOTest extends PlaySpec with OneAppPerSuite  {

  implicit override lazy val app = new GuiceApplicationBuilder().build

  val userDao = app.injector.instanceOf[UsersDao]

  "Example " should {
    "be valid" in {
      val result = userDao.read(1)
      println(Await.result(result), 1.second) 

      // would be better to use whenReady from org.scalatest.concurrent.Futures
      // but it's not really related to this question that much
    }
  }
}

概括

我认为这里的这种结论是避免手动构造对象(就像在第一种方法中一样 -UsersDao使用带有参数的构造函数创建)。如果您不需要这样做(有时您会这样做 - 例如当您想要更改某些参数时),大多数时候将整个对象构造委托给 DI 会更容易(例如 - 只需将UsersDao所有依赖项拉入已经注入)。

于 2017-01-09T10:30:52.307 回答