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 { } 
  } 
}
Simon
  • 4,844
  • 4
  • 32
  • 69
  • Can you post the source code which is causing the error? – kukido Oct 28 '15 at 14:33
  • The same question is asked here: http://stackoverflow.com/questions/31884182/play-2-4-2-play-slick-1-0-0-how-do-i-apply-database-evolutions-to-a-slick-man – Simon Oct 28 '15 at 16:28
  • 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: http://stackoverflow.com/questions/42368523/play-tests-with-database-too-many-connections/42416309#42416309. – JulienD Feb 23 '17 at 13:25

5 Answers5

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"))
}
Simon
  • 4,844
  • 4
  • 32
  • 69
  • Using this technique, after writing more tests I get a "Too many connections" error: http://stackoverflow.com/questions/42368523/play-tests-with-database-too-many-connections?noredirect=1#comment71900106_42368523. Any idea where the leak is? – JulienD Feb 22 '17 at 12:14
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
kukido
  • 9,895
  • 1
  • 39
  • 49
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

  • 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)` – Jonathan Lecointe Jun 07 '17 at 14:22
  • How can you have your database instance differently than the way I get it in the answer I provided? – Simon Jun 08 '17 at 08:42
  • The problem with your code is that since I use Slick I can get an instance of my database with play.api.db.DB – Simon Jun 08 '17 at 16:02
  • 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 linked – Jonathan Lecointe Jun 09 '17 at 09:03
  • It works for inMemoryDatabase with Slick like this: class InMemoryDbProvider extends DatabaseConfigProvider { def get[P <: basicprofile="" conf="" databaseconfig="" databaseconfigprovider.get="" db="inMemoryConfigProvider.get.db" fakeapplication.="" good="" in="" inmemoryconfigprovider="new" inmemorydbprovider="" lazy="" providing="" the="" val=""> – Simon Jun 09 '17 at 10:26
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.

jazmit
  • 4,464
  • 25
  • 36
  • 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. – Simon Oct 28 '15 at 15:34
  • 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. – jazmit Oct 28 '15 at 15:36
  • That's exactly what I want to do ! ;) (Not with an in-memory database but I don't think that it would be different.) – Simon Oct 28 '15 at 15:39
  • 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) – jazmit Oct 28 '15 at 15:45
  • 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. – Simon Oct 28 '15 at 15:49
  • OK, now I think I know what you're after, I'll add a different answer. – jazmit Oct 28 '15 at 16:05
  • 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) – Simon Oct 28 '15 at 16:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/93607/discussion-between-jazmit-and-user3683807). – jazmit Oct 28 '15 at 16:23
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 { }
jazmit
  • 4,464
  • 25
  • 36
  • 2
    The problem with your code is that since I use Slick I can get an instance of my database with play.api.db.DB – Simon Oct 28 '15 at 16:40