代码之家  ›  专栏  ›  技术社区  ›  steinybot

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

  •  6
  • steinybot  · 技术社区  · 7 年前

    fixture-context objects 具有 async testing 在ScalaTest中。

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

    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上下文对象,因为这允许我使用 stackable trait pattern .

    3 回复  |  直到 7 年前
        1
  •  3
  •   sfosdal    7 年前

    关于:

    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))
        }
      }
    }
    
        2
  •  0
  •   steinybot    7 年前

    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.
            }()
    
        3
  •  0
  •   lregnier    7 年前

    你可以有一个混合的 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))
        }
      }
    }