How to apply manually evolutions in tests with Slick and Play! 2.4
Asked Answered
I

5

12

I would like to manually run my evolution script at the beginning of each test file. I'm working with Play! 2.4 and Slick 3.

According to the documentation, the way to go seems to be:

Evolutions.applyEvolutions(database)

but I don't manage to get an instance of my database. In the documentation play.api.db.Databases is imported in order to get a database instance but if I try to import it, I get this error: object Databases is not a member of package play.api.db

How can I get an instance of my database in order to run the evolution script?

Edit: as asked in the comments, here is the entire source code giving the error:

import models._
import org.scalatest.concurrent.ScalaFutures._
import org.scalatest.time.{Seconds, Span}
import org.scalatestplus.play._
import play.api.db.evolutions.Evolutions
import play.api.db.Databases

class TestAddressModel extends PlaySpec with OneAppPerSuite {
   lazy val appBuilder = new GuiceApplicationBuilder()
   lazy val injector = appBuilder.injector()
   lazy val dbConfProvider = injector.instanceOf[DatabaseConfigProvider]

  def beforeAll() = {
    //val database: Database = ???
    //Evolutions.applyEvolutions(database)
  }

  "test" must { 
     "test" in { } 
  } 
}
Inevasible answered 28/10, 2015 at 13:52 Comment(3)
Can you post the source code which is causing the error?Footlambert
The same question is asked here: #31884682Inevasible
I had troubles with the answer below that seemed to work at first, and found a different solution that I reference here instead of copying: #42369023.Arthralgia
I
11

I finally found this solution. I inject with Guice:

lazy val appBuilder = new GuiceApplicationBuilder()

lazy val injector = appBuilder.injector()

lazy val databaseApi = injector.instanceOf[DBApi] //here is the important line

(You have to import play.api.db.DBApi.)

And in my tests, I simply do the following (actually I use an other database for my tests):

override def beforeAll() = {
  Evolutions.applyEvolutions(databaseApi.database("default"))
}

override def afterAll() = {
  Evolutions.cleanupEvolutions(databaseApi.database("default"))
}
Inevasible answered 28/10, 2015 at 18:51 Comment(1)
Using this technique, after writing more tests I get a "Too many connections" error: #42369023. Any idea where the leak is?Arthralgia
F
2

Considering that you are using Play 2.4, where evolutions were moved into a separate module, you have to add evolutions to your project dependencies.

libraryDependencies += evolutions
Footlambert answered 28/10, 2015 at 14:57 Comment(0)
H
1

To have access to play.api.db.Databases, you must add jdbc to your dependencies :

libraryDependencies += jdbc

Hope it helps some people passing here.

EDIT: the code would then look like this :

import play.api.db.Databases

val database = Databases(
  driver = "com.mysql.jdbc.Driver",
  url = "jdbc:mysql://localhost/test",
  name = "mydatabase",
  config = Map(
    "user" -> "test",
    "password" -> "secret"
  )
)

You now have an instance of the DB, and can execute queries on it :

val statement = database.getConnection().createStatement()
val resultSet = statement.executeQuery("some_sql_query")

You can see more from the docs

EDIT: typo

Halie answered 7/6, 2017 at 12:46 Comment(5)
My answer wasn't complete enough I guess. When you have this play.api.db.Database, you can create your Databases object like in the docs, and there you have your database instance, on which you can then use Evolutions.applyEvolutions(database)Halie
How can you have your database instance differently than the way I get it in the answer I provided?Inevasible
The problem with your code is that since I use Slick I can get an instance of my database with play.api.db.DBInevasible
Sure, but I saw in your first post that you had trouble using play.api.db.Databases, so I provided the answer. I had the same problem a couple days ago, and since I found the answer with great difficulty, I thought I would put it here to help people in the future. On another thought, your solution won't work for an in memory database, whereas mine can, with the appropriate changes you could find in play's docs I've linkedHalie
It works for inMemoryDatabase with Slick like this: class InMemoryDbProvider extends DatabaseConfigProvider { def get[P <: BasicProfile]: DatabaseConfig[P] = { DatabaseConfigProvider.get("test")(fakeAppWithInMemoryDb) } } lazy val inMemoryConfigProvider = new InMemoryDbProvider() lazy val db = inMemoryConfigProvider.get.db providing the good conf in fakeApplication.Inevasible
A
0

I find the easiest way to run tests with evolutions applied is to use FakeApplication, and input the connection info for the DB manually.

def withDB[T](code: => T): T =
  // Create application to run database evolutions
  running(FakeApplication(additionalConfiguration = Map(
    "db.default.driver"   -> "<my-driver-class>",
    "db.default.url"      -> "<my-db-url>",
    "db.default.user"     -> "<my-db>",
    "db.default.password" -> "<my-password>",
    "evolutionplugin"     -> "enabled"
    ))) {
    // Start a db session
    withSession(code)
  }

Use it like this:

"test" in withDB { }

This allows you, for example, to use an in-memory database for speeding up your unit tests.

You can access the DB instance as play.api.db.DB if you need it. You'll also need to import play.api.Play.current.

Axum answered 28/10, 2015 at 15:5 Comment(8)
I know how to enable autoEvolutions, what I want to do is to run the evolution script for each test file even if there was no changes (in the 1.sql file). That's why I want to have a database instance and I don't see where I can have it with your code. Moreover, I already set different configurations for my test with GuiceApplicationBuilder.Inevasible
Why don't you start with a blank DB for each test and apply all the evolutions? Otherwise you'll have issues if the tests don't put the DB back in exactly the right state.Axum
That's exactly what I want to do ! ;) (Not with an in-memory database but I don't think that it would be different.)Inevasible
I still don't understand why you need to directly access the database instance - I use H2DB and it drops the database after each test. However, it's available as play.api.db.DB once FakeApplication is running. (I know, horrible global state, the play devs are working on getting rid of it)Axum
Because I want to run my tests on a real PostgreSQL database especially because I use extension like postgis and citext. But like you I want to relaunch evolutions (downs and ups) at the beginning of each test.Inevasible
OK, now I think I know what you're after, I'll add a different answer.Axum
Yes you got it now, I'm now trying to make work the new snippet of code you added (without success for the moment, if I don't manage to make it work, I will comment your new response)Inevasible
Let us continue this discussion in chat.Axum
A
0

Use FakeApplication to read your DB configuration and provide a DB instance.

def withDB[T](code: => T): T =
  // Create application to run database evolutions
  running(FakeApplication(additionalConfiguration = Map(
       "evolutionplugin" -> "disabled"))) {
    import play.api.Play.current
    val database = play.api.db.DB
    Evolutions.applyEvolutions(database)
    withSession(code)
    Evolutions.cleanupEvolutions(database)
  }

Use it like this:

"test" in withDB { }
Axum answered 28/10, 2015 at 16:10 Comment(1)
The problem with your code is that since I use Slick I can get an instance of my database with play.api.db.DBInevasible

© 2022 - 2024 — McMap. All rights reserved.