我正在尝试在ScalaTest中使用fixture-context objects和async testing。
简单地将两者结合起来的天真方法不能编译。例如:
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 {
这是一个合理的错误,但我想知道在ScalaTest风格中解决这个问题的方法。
我想保留fixture-context对象,因为这样我就可以使用stackable trait pattern。
答案 0 :(得分:2)
怎么样:
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))
}
}
}
或者
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
}
}
trait SomeMoreFixture {
}
"Testing" should {
"be productive" in {
val fixture = new IntAdder with SomeMoreFixture
import fixture._
Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}
}
}
答案 1 :(得分:0)
到目前为止,我提出的最佳解决方案是执行以下操作:
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.
}()
答案 2 :(得分:0)
您可以混合使用fixture-context objects
和loan-fixtures methods
模式。
这样的事情:
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))
}
}
}