假设我有几个测试可以做这样的事情
"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
所以我的问题是我是否有可能做的事情,如果是的话,我的代码有什么问题,我需要改变什么才能让它发挥作用。
答案 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()
}