Async before and after for creating and dropping scala slick tables in scalatest
The following is the testing approach that Dennis Vriend takes in his slick-3.2.0-test project.
First, define a dropCreateSchema
method. This method attempts to create a table; if that attempt fails (because, for example, the table already exists), it drops, then creates, the table:
def dropCreateSchema: Future[Unit] = {
val schema = BlockHeaderTable.schema
db.run(schema.create)
.recoverWith {
case t: Throwable =>
db.run(DBIO.seq(schema.drop, schema.create))
}
}
Second, define a createEntries
method that populates the table with some sample data for use in each test case:
def createEntries: Future[Unit] = {
val setup = DBIO.seq(
// insert some rows
BlockHeaderTable ++= Seq(
BlockHeaderTableRow(/* ... */),
// ...
)
).transactionally
db.run(setup)
}
Third, define an initialize
method that calls the above two methods sequentially:
def initialize: Future[Unit] = for {
_ <- dropCreateSchema
_ <- createEntries
} yield ()
In the test class, mix in the ScalaFutures
trait. For example:
class TestSpec extends FlatSpec
with Matchers
with ScalaFutures
with BeforeAndAfterAll
with BeforeAndAfterEach {
// ...
}
Also in the test class, define an implicit conversion from a Future
to a Try
, and override the beforeEach
method to call initialize
:
implicit val timeout: Timeout = 10.seconds
implicit class PimpedFuture[T](self: Future[T]) {
def toTry: Try[T] = Try(self.futureValue)
}
override protected def beforeEach(): Unit = {
blockHeaderRepo.initialize // in this example, initialize is defined in a repo class
.toTry recover {
case t: Throwable =>
log.error("Could not initialize the database", t)
} should be a 'success
}
override protected def afterAll(): Unit = {
db.close()
}
With the above pieces in place, there is no need for Await
.
Unfortunately, @Jeffrey Chung's solution hanged for me (since futureValue
actually awaits internally). This is what I ended up doing:
import org.scalatest.{AsyncFreeSpec, FutureOutcome}
import scala.concurrent.Future
class TestTest extends AsyncFreeSpec /* Could be any AsyncSpec. */ {
// Do whatever setup you need here.
def setup(): Future[_] = ???
// Cleanup whatever you need here.
def tearDown(): Future[_] = ???
override def withFixture(test: NoArgAsyncTest) = new FutureOutcome(for {
_ <- setup()
result <- super.withFixture(test).toFuture
_ <- tearDown()
} yield result)
}