如何在ScalaTest中使用具有异步规范的夹具上下文对象?

如何在ScalaTest中使用具有异步规范的夹具上下文对象?,scala,unit-testing,asynchronous,scalatest,Scala,Unit Testing,Asynchronous,Scalatest,我试图在ScalaTest中使用with 简单地将两者结合起来的天真方法无法编译。例如: import org.scalatest.AsyncWordSpec import scala.collection.GenTraversableOnce import scala.concurrent.{ExecutionContext, Future} import scala.math.Numeric.IntIsIntegral trait Adder[T] { implicit def nu

我试图在ScalaTest中使用with

简单地将两者结合起来的天真方法无法编译。例如:

import org.scalatest.AsyncWordSpec

import scala.collection.GenTraversableOnce
import scala.concurrent.{ExecutionContext, Future}
import scala.math.Numeric.IntIsIntegral

trait Adder[T] {
  implicit def num: Numeric[T]
  def add(number: T): Unit
  def result: Future[T]
}

object Foo {
  def doubleSum[T](adder: Adder[T], numbers: GenTraversableOnce[T])(implicit ec: ExecutionContext): Future[T] = {
    numbers.foreach(adder.add)
    val num = adder.num
    import num._
    adder.result.map(result => result + result)
  }
}

class FooSpec extends AsyncWordSpec {

  trait IntAdder {
    val adder = new Adder[Int] {
      override implicit val num = IntIsIntegral
      private var sum = Future.successful(num.zero)
      override def add(number: Int): Unit = sum = sum.map(_ + number)
      override def result: Future[Int] = sum
    }
  }

  "Testing" should {
    "be productive" in new IntAdder {
      Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }
  }
}
无法通过以下方式编译:

Error:(37, 11) type mismatch;
found   : FooSpec.this.IntAdder
required: scala.concurrent.Future[org.scalatest.compatible.Assertion]
          new IntAdder {
这是一个合理的错误,但我想知道有什么方法可以以最具规模的方式解决这个问题


我想保留fixture上下文对象,因为它允许我使用。

到目前为止,我提出的最佳解决方案是执行以下操作:

class FooSpec extends AsyncWordSpec {

  trait IntAdder {
    ... // Same as in the question
    val assertion: Future[compatible.Assertion]
  }

  "Testing" should {
    "be productive" in new IntAdder {
      val assertion = Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }.assertion
  }
}
我希望将其稍微降低到:

class FooSpec extends AsyncWordSpec {

  trait IntAdder extends (() => Future[compatible.Assertion]) {
    ... // Same as in the question
    val assertion: Future[compatible.Assertion]
    override def apply(): Future[Assertion] = assertion
  }

  "Testing" should {
    "be productive" in new IntAdder {
      val assertion = Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }()
  }
}
但是,由于以下原因,这也无法编译:

Error:(42, 10) ';' expected but '(' found.
        }()
那么:

import org.scalatest.compatible.Assertion

class FooSpec extends AsyncWordSpec {

  def withIntAdder(test: Adder[Int] => Future[Assertion]): Future[Assertion] = {
     val adder = new Adder[Int] { ... }
     test(adder)
  }

  "Testing" should {
    "be productive" in withIntAdder { adder =>
      Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }
  }
}


您可以混合使用
fixture上下文对象
loan fixture方法
模式

大概是这样的:

class FooSpec extends AsyncWordSpec {

// Fixture-context object 
trait IntAdder {
    val adder = new Adder[Int] {
    override implicit val num = IntIsIntegral
    private var sum = Future.successful(num.zero)
    override def add(number: Int): Unit = sum = sum.map(_ + number)
    override def result: Future[Int] = sum
  }
}

// Loan-fixture method
def withContext(testCode: IntAdder => Future[compatible.Assertion]): Future[compatible.Assertion] = {
  val context = new IntAdder {}
  testCode(context)
}

"Testing" should {
    "be productive" in withContext { context =>
      import context._
      Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }
  }
}

这就是模式。这是可行的,但我对fixture context对象更感兴趣,因为您可以使用stackable trait模式,并且可以自动获取范围中的所有成员,而不必将它们全部声明为参数。我在问题中阐明了这一要求。并非每个ScalaTest样式都支持
fixture context对象
。特别是
AsyncWordSpec
没有。请参阅scaladoc,它未在“共享设备”部分中列出。你可以考虑在套件级别上使用可堆叠的特征模式(参见SCADADOC中的“按固定特征叠加固定”和嵌套套件)。否则,我担心您必须实施您自己的解决方案,就像您在自己的答案中尝试的那样。或者只是创建您的可堆叠夹具,然后像我更新的答案一样导入?很好,它不在scaladoc中,我想我已经看过了。我喜欢你的第二个导入解决方案。它似乎以最小的开销保留了所有的好处。
class FooSpec extends AsyncWordSpec {

// Fixture-context object 
trait IntAdder {
    val adder = new Adder[Int] {
    override implicit val num = IntIsIntegral
    private var sum = Future.successful(num.zero)
    override def add(number: Int): Unit = sum = sum.map(_ + number)
    override def result: Future[Int] = sum
  }
}

// Loan-fixture method
def withContext(testCode: IntAdder => Future[compatible.Assertion]): Future[compatible.Assertion] = {
  val context = new IntAdder {}
  testCode(context)
}

"Testing" should {
    "be productive" in withContext { context =>
      import context._
      Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
    }
  }
}