如何编写异步Scala.js测试(例如使用ScalaTest)?

时间:2017-03-01 09:16:19

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

我的一些代码是异步的,我想测试这段代码的执行导致了正确的状态。我没有引用可以映射的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,但是根据上面某个链接中的评论判断,uTest似乎有类似的问题,除非它倾向于忽略测试而不是拖延测试套件。

我只是想在短暂的延迟之后做出断言,看起来肯定是可能的。关于如何实现这一点的任何建议都将非常感激。

2 个答案:

答案 0 :(得分:1)

正如我在this scala.js gitter thread中向我解释的那样,我错误地使用了Promise.success。该方法需要一个值来完成promise,但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的{​​{1}}。 Try接受按名称传递模式的参数,这意味着只有在调用Try.apply之后才会对其进行评估。

所以工作代码如下所示:

Try()

答案 1 :(得分:0)

真正的答案是:您应该尝试将单元测试设置的“异步”部分 out

所有处理等待;睡觉;等等会增加您在单元测试中想要所具有的复杂程度。

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

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

长话短说:考虑一下那个让你在解决方案中“异步”的“概念”;如果有办法避免“真正的异步”部分;但当然没有(!)对您的生产代码进行仅测试更改。