Loading test data with ScalaTest + Play

comment 1
Dev

The ScalaTest + Play library provides a couple of useful traits for when your ScalaTest Play Framework functional tests need a running application for context. The OneAppPerSuite trait will share the same Application instance across all tests in a class whereas the OneAppPerTest trait gives each test its own Application instance.

These traits will ensure you have a running application for your tests but if you want to test code that operates on a database it can be helpful to load some known test data before each test and clean it up afterwards. For this, we can mix in a ScalaTest before-and-after trait.

The BeforeAndAfter trait lets you define a piece of code to run before each test with before and/or after each test with after. There is also a BeforeAndAfterAll trait which invokes methods before and after executing the suite but resetting the database for each test makes for better test isolation.

Here is a base test class that sets up a running application and uses the Evolutions companion object to load and clean up the database. Note this class uses Guice dependency injection to retrieve the database object but you can also easily connect to a database using the Database companion object.

We also override the db.default.url config value to point to a test database.

import org.scalatest.BeforeAndAfter
import org.scalatestplus.play.{OneAppPerSuite, PlaySpec}
import play.api.Application
import play.api.db.Database
import play.api.db.evolutions.{Evolution, Evolutions, SimpleEvolutionsReader}
import play.api.inject.guice.GuiceApplicationBuilder

abstract class IntegrationSpec extends PlaySpec with OneAppPerSuite with BeforeAndAfter {

  implicit override lazy val app: Application = new GuiceApplicationBuilder()
    .configure("db.default.url" -> sys.env.getOrElse("DB_TEST_URL", "jdbc:mysql://localhost:3306/my_test_db?useSSL=false"))
    .build

  before {
    val db = app.injector.instanceOf[Database]

    // Load the database schema
    Evolutions.applyEvolutions(db)

    // Insert test data
    Evolutions.applyEvolutions(db, SimpleEvolutionsReader.forDefault(
      Evolution(
        999,
        "insert into test (name, amount) values ('test', 0.0);",
        "delete from test;"
      )
    ))
  }

  after {
    val db = app.injector.instanceOf[Database]
    Evolutions.cleanupEvolutions(db)
  }
}

You can also load evolutions from the file system if you don’t want to define them in the code.

1 Comment

  1. Moritz says

    Hi David,

    thanks for sharing this, put me in the right direction with a testing-problem. I think, the Scala-community could profit a lot from those small, but complete (with imports and all) snippets and examples!

Leave a Reply

Your email address will not be published. Required fields are marked *