数据库事务中的 Specs2 包装单元测试

Specs2 wrap unit test in database transaction

我正在将我的应用程序从 Specs2 2.3.12 更新到 3.6.1,并且在更新 class 时遇到问题,它将我们的单元测试包装在一个事务中。

2.3.12 class:

class DbTx extends AroundOutside[Session] {
  var session: Option[Session] = None
  def around[T : AsResult](t: => T) = {
    Db.withTransaction { implicit s =>
      session = Some(s)
      val result = AsResult(t)
      s.rollback()
      result
    }
  }
  def outside: Session = session.get
}

它的用法:

"my unit test" in (new DbTx).apply { implicit session: Session =>
  ...
}

我在 3.6.1

中尝试过的
class DbTx extends ForEach[Session] {
  var session: Option[Session] = None
  def foreach[T : AsResult](t: Session => T) = {
    Db.withTransaction { implicit s =>
      session = Some(s)
      val result = AsResult(t)
      s.rollback()
      result
    }
  }
}

它的用法:

"my unit test" in (new DbTx).foreach { implicit session: Session =>
  ...
}

但这似乎在该块的第 6 行和第 4 行之间产生了无限循环。

我也试过了

class DbTx extends Around {
  def around[T: AsResult](t: => T): Result = {
    super.around {
      Db.withTransaction { implicit s: Session =>
        val result = AsResult(t)
        s.rollback()
        result
      }
    }
  }
}

它的用法:

"my unit test" in (new DbTx).around { implicit session: Session =>
  ...
}

但这会导致

could not find implicit value for evidence parameter of type AsResult[Session => MatchResult[ ... ]]

我也试过了

class DbTx extends Fixture[Session] {
  def apply[T: AsResult](t: Session => T): Result = {
    Db.withTransaction { implicit s: Session =>
      val result = AsResult(t)
      s.rollback()
      result
    }
  }
}

它的用法:

"my unit test" in (new DbTx) { implicit session: Session =>
  ...
}

结果是

could not find implicit value for evidence parameter of type AsResult[Session => T]

编辑

我也遇到了这个代码的无限循环:

import org.specs2.execute.AsResult
import org.specs2.mutable.Specification
import org.specs2.specification.ForEach

class DbTxSpec extends Specification with ForEach[Session] {
  def foreach[T: AsResult](t: Session => T) = {
    Db.withTransaction { implicit s =>  // infinite loop between here
      try AsResult(t)                   // and here
      finally s.rollback()
    }
  }

  "my unit test" in { implicit session: Session =>
    true must beTrue
  }
}

如果你想传递一个 Session 你需要使用你的规范扩展 ForEach 特性,而不是一个特殊的对象。类似于:

class DbTxSpec extends Specification with ForEach[Session] {
  var session: Option[Session] = None

  def foreach[T : AsResult](t: Session => T) = {
    Db.withTransaction { implicit s =>
      session = Some(s)
      try AsResult(t(session))
      finally s.rollback()
    }
  }

  "my unit test" in { implicit session: Session =>
    ...
  }
}