2017-03-01 57 views
1

我的一些代码是异步的,我想测试这个代码的执行导致了正确的状态。我没有提及可以映射的Future或JS Promise的参考 - 所讨论的异步代码存在于我正在使用的JS库中,它只是调用setTimeout(setSomeState, 0),这就是为什么我唯一的追求是测试该状态在短暂延迟(10毫秒)后异步。如何编写异步Scala.js测试(例如使用ScalaTest)?

这是我最好的尝试:

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有一个类似的问题,除了它倾向于忽略测试而不是拖延测试套件。

我只是想在短暂的延迟后作出断言,似乎应该是绝对有可能的。任何有关如何实现这一目标的建议都将非常感激。

回答

1

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

由于在我的代码中assert(false)在调用Promise.success之前被评估,所以在承诺有机会完成之前抛出异常。但是,该异常在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接受通过名称模式的参数,这意味着它将仅在调用Try()后才被评估。

所以工作代码如下所示:

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

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

所有处理等待;睡觉;等等增加了一个复杂度,你不需要想要在你的单元测试中有。

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

示例:当您在Java的ExecutorService上构建线程时,您可以使用same-thread执行程序服务;你的单元测试正在使用单线程;许多事情变得更容易。长话短说:考虑一下在你的解决方案中给你“异步”的那个“概念”。如果有办法避免“真正异步”的部分;但是当然没有(!)对您的生产代码进行仅测试更改。

+0

对不起,我不明白我怎么可以把这个给我的处境。我在JS库里面调用了一个'baz'方法,里面有这个JS代码:'setTimeout(setSomeState,0)'。我需要声明'setSomeState'已经对我在测试范围内的变量进行了必要的修改。在我上面的例子中,我把它抽象为'assert(true)'。但是这个断言显然需要在* setSomeState运行之后发生,因此断言必须是异步的。 – Nikita

+0

嗯,这更像是一个通用的想法......当它不适用时,我可以删除答案。 – GhostCat

相关问题