I have the following DAO that connects to a database using PlaySlick. The class has a method read
that I need to test with ScalaTest. My problem is that I don't know how to mock DatabaseConfigProvider
to inject it in the UsersDAO
class and test the read
method. This is the class to test:
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)
}
}
}
and this is my attempt to write the test:
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)
}
}
When I run the test it fails with the following error message:
com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for play.api.db.slick.DatabaseConfigProvider was bound. while locating play.api.db.slick.DatabaseConfigProvider
IMHO, it is best not to interfere with Play's injection stuff, but to just use it. This should work:
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)
}
}
}
}
I've updated my repo in case I missed something.
I am not sure you want to mock database altogether from your tests or simply use different database configuration.
Looking at your code sample I assume the latter.
If you really would like to do it as you started, the simplest solution would be this:
// instead of your line below
// val dbConfigProvider = app.injector.instanceOf[DatabaseConfigProvider]
// use this:
val userDao = app.injector.instanceOf[UsersDao]
Above will inject your DAO and implicitly deal with your DatabaseConfigProvider
.
But I don't understand here one thing - why don't you just create another configuration (application.conf
) in your test resources with contents like this:
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"
After doing this just change your app
creation to this:
implicit override lazy val app = new GuiceApplicationBuilder().build
and just normally inject UsersDao
like this (same way as above):
val usersDao = app.injector.instanceOf[UsersDao]
?
(I am assuming above that you are just having different db configuration in test and in app).
Full code
test/resource/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
}
}
}
Summary
I think that kind of a conclusion here would be to avoid constructing objects by hand (like in the first approach - creating UsersDao
with constructor with parameter). If you don't need to do it (sometimes you do - when e.g. you want to change some of the parameters) it's most of the time easier to just delegate the whole object construction to DI (like - simply pull UsersDao
with all dependencies already injected).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With