如何编写异步 Scala.js 测试(例如使用 ScalaTest)?
How to write async Scala.js test (e.g. using ScalaTest)?
我的一些代码是异步的,我想测试这段代码的执行是否导致了正确的状态。我没有对我可以映射的 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
行,我会得到 "Queue is empty while future is not completed, this means you're probably using a wrong ExecutionContext for your task, please double check your Future." 错误,下面的链接之一对此进行了详细解释。
我认为这些链接与此问题相关:
https://github.com/scalatest/scalatest/issues/910
https://github.com/scalatest/scalatest/issues/1039
但我想不出可行的解决方案。
我应该以不同的方式构建 Future[Assertion]
吗?
我与 ScalaTest 无关,但从上面一个链接中的评论来看,uTest 似乎也有类似的问题,只是它倾向于忽略测试而不是停止测试套件。
我只是想在短暂的延迟之后做出断言,看起来应该是绝对可能的。任何关于如何实现这一点的建议将不胜感激。
这里的真实答案是:您应该尝试获取单元测试设置的"async"部分out。
所有处理等待;睡觉;等等增加了您不希望在您的单元测试中拥有的复杂程度。
由于您没有展示您正在使用的生产代码,我只能就如何在一般层面上处理这个主题提出一些建议。
示例:当一个人在 Java 的 ExecutorService 之上构建他的线程时,您可以寻求 same-thread 执行程序服务;并且您的单元测试使用的是单线程;许多事情变得容易多了。
长话短说:考虑研究 "concept" 给你 "async" 的解决方案;以及是否有办法避免 "really async" 部分;但当然没有(!)对您的生产代码进行仅测试更改。
正如在 this scala.js gitter thread 中向我解释的那样,我使用 Promise.success
不正确。该方法需要一个值来完成承诺,但 assert(false)
抛出异常,它不会 return 类型 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
,它期望 Try
。 Try.apply
接受按名称传递模式的参数,这意味着它只会在 Try()
被调用后被评估。
因此工作代码如下所示:
it("async works") {
val promise = Promise[Assertion]()
js.timers.setTimeout(10) {
promise.complete(Try({
println("FOO")
assert(true)
})
})
promise.future
}
我的一些代码是异步的,我想测试这段代码的执行是否导致了正确的状态。我没有对我可以映射的 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
行,我会得到 "Queue is empty while future is not completed, this means you're probably using a wrong ExecutionContext for your task, please double check your Future." 错误,下面的链接之一对此进行了详细解释。
我认为这些链接与此问题相关:
https://github.com/scalatest/scalatest/issues/910
https://github.com/scalatest/scalatest/issues/1039
但我想不出可行的解决方案。
我应该以不同的方式构建 Future[Assertion]
吗?
我与 ScalaTest 无关,但从上面一个链接中的评论来看,uTest 似乎也有类似的问题,只是它倾向于忽略测试而不是停止测试套件。
我只是想在短暂的延迟之后做出断言,看起来应该是绝对可能的。任何关于如何实现这一点的建议将不胜感激。
这里的真实答案是:您应该尝试获取单元测试设置的"async"部分out。
所有处理等待;睡觉;等等增加了您不希望在您的单元测试中拥有的复杂程度。
由于您没有展示您正在使用的生产代码,我只能就如何在一般层面上处理这个主题提出一些建议。
示例:当一个人在 Java 的 ExecutorService 之上构建他的线程时,您可以寻求 same-thread 执行程序服务;并且您的单元测试使用的是单线程;许多事情变得容易多了。
长话短说:考虑研究 "concept" 给你 "async" 的解决方案;以及是否有办法避免 "really async" 部分;但当然没有(!)对您的生产代码进行仅测试更改。
正如在 this scala.js gitter thread 中向我解释的那样,我使用 Promise.success
不正确。该方法需要一个值来完成承诺,但 assert(false)
抛出异常,它不会 return 类型 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
,它期望 Try
。 Try.apply
接受按名称传递模式的参数,这意味着它只会在 Try()
被调用后被评估。
因此工作代码如下所示:
it("async works") {
val promise = Promise[Assertion]()
js.timers.setTimeout(10) {
promise.complete(Try({
println("FOO")
assert(true)
})
})
promise.future
}