在交易中包装测试

时间:2015-04-20 15:14:03

标签: scala playframework

假设我有几个测试可以做这样的事情

"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()
    }
}

我不想记得在事务中包装所有数据库测试,并在每次测试结束时手动调用con.rollback()

相反,我尝试编写一个特征,将整个测试包装在一个事务中,这样我就可以像这样编写我的测试

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

这是我到目前为止所拥有的

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
            }
    }
}

但是,当我尝试运行上面的测试时,我收到此错误

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

所以我的问题是我是否有可能做的事情,如果是的话,我的代码有什么问题,我需要改变什么才能让它发挥作用。

2 个答案:

答案 0 :(得分:1)

Scala中的Implicits并不像在Java应用程序服务器中添加到某种上下文中的事务那样神奇,相反,它们通过告诉编译器来工作"如果缺少类型T的此参数,请查看范围在我用于T型值的地方,标记为隐式"。

这就是为什么您的常用代码看起来像这样:

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

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

但是这也意味着你不能免费获得它,因为你继承的某个类有一个隐式连接,它需要在范围内可用,并且Around不允许你改变测试的签名,所以你不能真的用它来进入范围。

这样做的一种方法可能是避免使用withTransaction而是在范围内使用DB.getConnection,就像这样(从头顶开始,所以可能不是精确/编译):

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

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

然后在测试中使用它,它将隐式连接到范围:

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

答案 1 :(得分:0)

基于johanandren's answer,我提出了以下解决方案

trait DatabaseIsolation extends Scope with After {

    implicit val app: FakeApplication = FakeApplication()

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

    override def after = con.rollback()

}

然后在测试中使用它

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