【问题标题】:How to write async Scala.js test (e.g. using ScalaTest)?如何编写异步 Scala.js 测试(例如使用 ScalaTest)?
【发布时间】:2017-07-20 12:57:59
【问题描述】:

我的一些代码是异步的,我想测试这段代码的执行是否导致了正确的状态。我没有可以映射的 Future 或 JS Promise 的引用——有问题的异步代码位于我正在使用的 JS 库中,它只是调用setTimeout(setSomeState, 0),这就是为什么我唯一的办法是在短暂的延迟(10 毫秒)之后异步测试状态。

这是我最好的尝试:

import org.scalatest.{Assertion, AsyncFunSpec, Matchers}    
import scala.concurrent.Promise
import scala.scalajs.js
import scala.scalajs.concurrent.JSExecutionContext

class FooSpec extends AsyncFunSpec with Matchers {

  implicit override def executionContext = JSExecutionContext.queue

  it("async works") {
    val promise = Promise[Assertion]()

    js.timers.setTimeout(10) {
      promise.success {
        println("FOO")
        assert(true)
      }
    }

    promise.future
  }
}

这在断言成功时有效——使用assert(true)。但是,当断言失败时(例如,如果您将其替换为 assert(false)),测试套件会冻结。 sbt 只是停止打印任何东西,并无限期地挂起,测试套件永远不会完成。如果出现此类故障,FooSpec: 行会打印出来,但不会打印测试名称 ("async works"),也不会打印 "FOO" 字符串。

如果我注释掉 executionContext 行,我会得到“队列为空,而未来未完成,这意味着您可能为您的任务使用了错误的 ExecutionContext,请仔细检查您的未来。”以下链接之一详细解释了错误。

我认为这些链接与这个问题有关:

https://github.com/scalatest/scalatest/issues/910

https://github.com/scalatest/scalatest/issues/1039

但我想不出可行的解决方案。

我应该以不同的方式构建Future[Assertion] 吗?

我与 ScalaTest 无关,但从上面其中一个链接中的 cmets 判断,uTest 似乎也有类似的问题,只是它倾向于忽略测试而不是停止测试套件。

我只是想在短暂的延迟后做出断言,似乎这绝对是可能的。任何关于如何实现这一点的建议将不胜感激。

【问题讨论】:

    标签: scala unit-testing asynchronous scalatest scala.js


    【解决方案1】:

    这里的真正答案是:您应该尝试退出单元测试设置的“异步”部分。

    所有处理等待;睡觉;等等会增加您在单元测试中不希望拥有的复杂程度。

    由于您没有显示您正在使用的生产代码,我只能就如何在一般层面上处理这个主题提出一些建议。

    示例:当一个人在 Java 的 ExecutorService 之上构建线程时,您可以使用 same-thread 执行器服务;并且您的单元测试使用单个线程;许多事情变得容易得多。

    长话短说:考虑研究在您的解决方案中为您提供“异步”的“概念”;以及是否有办法避免“真正异步”的部分;但当然不需要 (!) 对您的生产代码进行仅测试更改。

    【讨论】:

    • 抱歉,我不明白如何将其应用于我的情况。我在一个 JS 库上调用了一个方法 baz,它里面有这个 JS 代码:setTimeout(setSomeState, 0)。我需要断言setSomeState 已对我在测试范围内的变量进行了所需的更改。在上面的示例中,我将其抽象为assert(true)。但是这个断言显然需要在 setSomeState 运行之后发生,因此断言必须是异步的。
    • 嗯,这更像是一个笼统的想法......当它不适用时,我可以删除答案。
    【解决方案2】:

    正如在this scala.js gitter thread 中向我解释的那样,我错误地使用了Promise.success。该方法需要一个值来完成承诺,但 assert(false) 抛出异常,它不返回 Assertion 类型的值。

    由于在我的代码中assert(false) 在调用Promise.success 之前被评估,所以在promise 有机会完成之前抛出异常。但是,异常是在 setTimeout 的同步回调中​​抛出的,因此对 ScalaTest 是不可见的。然后 ScalaTest 等待一个永远不会完成的promise.future(因为底层的承诺永远不会完成)。

    基本上我的代码是这样的:

    val promise = Promise[Assertion]()
    js.timers.setTimeout(10) {
      println("FOO")
      val successValue = assert(false) // exception thrown here
      promise.success(successValue) // this line does not get executed
    }
    promise.future
    

    相反,我应该使用 Promise.complete,它需要 TryTry.apply 在 pass-by-name 模式下接受参数,这意味着只有在调用 Try() 后才会对其进行评估。

    所以工作代码如下所示:

    it("async works") {
      val promise = Promise[Assertion]()
      js.timers.setTimeout(10) {
        promise.complete(Try({
          println("FOO")
          assert(true)
        })
      })
      promise.future
    }
    

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多