Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply play-evolutions when running tests in play-framework?

I have problems with evolutions when running tests in play framework using

  • playframework v2.6.6 for scala
  • play-slick v3.0.2
  • play-slick-evolutions v3.0.2

The test looks like this:

class TestFooController extends PlaySpec with GuiceOneServerPerSuite {
  "foo endpoint should store some data" in {
    val wsClient = app.injector.instanceOf[WSClient]
    val url = s"http://localhost:$port/foo"
    val requestData = Json.obj("foo" -> "bar")
    val response = await(wsClient.url(url).post(requestData))
    response.status mustBe OK
  }
}

The database configuration looks like this:

slick.dbs.default.driver="slick.driver.H2Driver$"
slick.dbs.default.db.driver="org.h2.Driver"
slick.dbs.default.db.url="jdbc:h2:mem:play"

Asume there is an evolution script which creates the table foos and this script is working fine in dev mode.

When running the test the following error is thrown:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[JdbcSQLException: Table "foos" not found;

The table foos could not be found so I assume that database evolutions have not been applied.

Then I changed the database configuration to postgresql which is used in dev mode.

slick.dbs.default.driver = "slick.driver.PostgresDriver$"
slick.dbs.default.db.driver = "org.postgresql.Driver"
slick.dbs.default.db.url = "jdbc:postgresql://localhost:5432/foo-test"
slick.dbs.default.db.user = "user"
slick.dbs.default.db.password = "password"

With this configuration the test work fine and data are stored in the database, so database evolutions ran just fine.

Now the problem is, that the database is not cleaned up after tests. I'd like to run each test suite with a clean database.

To sum up. With H2Db evolutions are not applied, with postgresql evolutions are applied but not cleaned up.

Even if this explicitly defined in application.test.conf

play.evolutions.autoApply=true
play.evolutions.autoApplyDowns=true

I also tried

play.evolutions.db.default.autoApply=true
play.evolutions.db.default.autoApplyDowns=true

no effect.

Then I tried to do this manually via:

  def withManagedDatabase[T](block: Database => T): Unit = {
    val dbapi    = app.injector.instanceOf[DBApi]
    val database = dbapi.database("default")
    Evolutions.applyEvolutions(database)
    block(database)
    Evolutions.cleanupEvolutions(database)
  }

and then changing the test to:

  "foo endpoint should store some data" in withManagedDatabase { _ =>
    ...
  }

For the H2 database configuration it has no effect, the same error that table foos can not be found is thrown. For the postgresql database configuration an evolution exceptions is thrown

play.api.db.evolutions.InconsistentDatabase: Database 'default' is in an inconsistent state![An evolution has not been applied properly. Please check the problem and resolve it manually before marking it as resolved.]

I want evolution ups running before and evolution downs running after each test suite. How can this be achieved?

like image 570
schub Avatar asked Oct 27 '17 22:10

schub


2 Answers

This is working for me:

class DAOSpec extends PlaySpec with GuiceOneAppPerSuite {

  val dbUrl = sys.env.getOrElse("DATABASE_URL", "postgres://foo:password@localhost:5432/foo")

  val testConfig = Map("db.default.url" -> dbUrl)

  implicit override def fakeApplication() = new GuiceApplicationBuilder().configure(testConfig).build()

  lazy val database = app.injector.instanceOf[Database]
  lazy val dao = app.injector.instanceOf[DAO]

  "create" must {
    "work" in Evolutions.withEvolutions(database) {
      val foo = await(dao.create("foo"))
      foo.id must not be null
    }
  }

}
like image 78
James Ward Avatar answered Oct 07 '22 12:10

James Ward


You can use the following to apply evolutions before each suite and clean up afterwards:

trait DatabaseSupport extends BeforeAndAfterAll {
  this: Suite with ServerProvider =>

  private lazy val db = app.injector.instanceOf[DBApi]

  override protected def beforeAll(): Unit = {
    super.beforeAll()
    initializeEvolutions(db.database("default"))
  }

  override protected def afterAll(): Unit = {
    cleanupEvolutions(db.database("default"))
    super.afterAll()
  }

  private def initializeEvolutions(database: Database):Unit = {
    Evolutions.cleanupEvolutions(database)
    Evolutions.applyEvolutions(database)
  }

  private def cleanupEvolutions(database: Database):Unit = {
    Evolutions.cleanupEvolutions(database)
  }

}

like image 39
Rutger Claes Avatar answered Oct 07 '22 12:10

Rutger Claes