Specs2 wrap unit test in database transaction

148 views Asked by At

I'm updating my application from Specs2 2.3.12 to 3.6.1 and am having trouble updating the class which wraps our unit tests in a transaction.

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

its usage:

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

What I've tried in 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
    }
  }
}

its usage:

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

but this seemed to produce an infinite loop between lines 6 & 4 of that block.

I also tried

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

its usage:

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

but that results in

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

I also tried

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

its usage:

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

which results in

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

Edit

I'm also getting an infinite loop with this code:

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

There are 1 answers

2
Eric On BEST ANSWER

If you want to pass in a Session you need to use have your specification extend the ForEach trait, not a special object. Something like:

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