Wrapping the test in a transaction

Assuming I have some tests that do something like this

"should do something with the database" in new WithApplication {
    DB.withTransaction { implicit con =>
        // Query the database.
        // Perform my tests.

        // Rollback the transaction to return database to initial state
        con.rollback()
    }
}

      

I don't want to forget to transfer all my database tests into a transaction and call them con.rollback()

manually at the end of each test.

Instead, I tried to write a dash that wraps the whole test in a transaction, so I could just write my tests like this

"do something to the database" in new WithApplication with DatabaseTest {
    SQL("...").execute()
}

      

This is what I still have

trait DatabaseTest extends Around with Scope {
    abstract override def around[T: AsResult](test: => T): Result = {
        super.around = {
            import play.api.Play.current

            DB.withTransaction { implicit con =>
                val result = test
                con.rollback()
                result
            }
    }
}

      

However, when I try to run the test above I get this error

could not find implicit value for parameter connection: java.sql.Connection

      

So my question is if what I am trying to do is possible, and if so, what is wrong with my code and what I need to change to make it work.

+3


source to share


2 answers


Implicits in Scala are not as small as transactions added to some context on Java application servers, for example, instead they work by telling the compiler "if this parameter of type T is missing, look around where I is used for a value of type T. marked as implicit.

This is why your normal code that looks like this:

def doStuff()(implicit aConnection: Connection) = ???

DB.withTransaction { implicit theConnection =>
    doStuff() // secretly the compiler adds theConnection here
}

      

But that also means you can't get it for free, because some class you inherited has an implicit join, it must be scope-accessible and Around doesn't let you change the test signature, so you can't really get its in the field using this.



One way to do this might be to avoid withTransaction and instead use DB.getConnection in scope, something like this (from the top of your head, so it might not be accurate / compile):

trait Tx extends Scope with After {
  implicit lazy val conn = DB.getConnection(autocommit = false)

  override def after() = conn.rollback()
}

      

Then use it in your tests, which will be implicitly scoped:

"something something" in new Tx {
   SQL("...").execute()
}

      

+1


source


Based on johanandren's answer I came up with the following solution

trait DatabaseIsolation extends Scope with After {

    implicit val app: FakeApplication = FakeApplication()

    implicit lazy val con = DB.getConnection(autocommit = false)

    override def after = con.rollback()

}

      



And then use it in a test like this

"do something" in new DatabaseIsolation {
    SQL("...").execute()
}

      

0


source







All Articles