如何在Scala单元测试中指定异步调用的确切执行顺序?

时间:2018-09-23 12:51:37

标签: scala concurrency future execution

我为Scala的期货编写了许多不同的单元测试。 所有异步调用都使用执行上下文。 为了确保异步调用始终以相同的顺序执行,我需要延迟一些非常困难的任务,这会使测试变慢。 执行者可能仍然(取决于其实现)先于其他任务完成。

以特定执行顺序测试并发代码的最佳方法是什么?例如,我有以下测试案例:

"firstSucc" should "complete the final future with the first one" in {
    val executor = getExecutor
    val util = getUtil
    val f0 = util.async(executor, () => 10)
    f0.sync
    val f1 = util.async(executor, () => { delay(); 11 })
    val f = f0.firstSucc(f1)
    f.get should be(10)
  }

其中延迟为def delay() = Thread.sleep(4000),而sync则使将来同步(呼叫Await.ready(future, Duration.Inf))。 这就是我要确保f0已经完成并且f1在f0之后完成的方式。仅凭firstSucc可能会使期货改组,因此仅完成f0是不够的。因此,应该将f1延迟到检查f.get之后。

另一个想法是从承诺中创建期货并在特定的时间点完成它们:

  "firstSucc" should "complete the final future with the first one" in {
    val executor = getExecutor
    val util = getUtil
    val f0 = util.async(executor, () => 10)
    val p = getPromise
    val f1 = p.future
    val f = f0.firstSucc(f1)
    f.get should be(10)
    p.trySuccess(11)
  }

是否有更容易/更好的方法来定义执行顺序?也许是另一种执行服务,可以在其中配置已提交任务的顺序? 对于这种特定情况,将第二个期货推迟到检查结果之后可能就足够了,但是在某些情况下,必须按一定顺序完成所有期货。

完整的代码可以在这里找到:https://github.com/tdauth/scala-futures-promises

测试用例是此类的一部分:https://github.com/tdauth/scala-futures-promises/blob/master/src/test/scala/tdauth/futuresandpromises/AbstractFutureTest.scala

由于Scala可以使用Java Executor服务,所以此问题可能相关:Controlling Task execution order with ExecutorService

1 个答案:

答案 0 :(得分:0)

对于大多数简单情况,我想说一个线程执行器就足够了-如果您一个接一个地执行期货,它们将被串行执行,并以相同的顺序完成。

但是看来您的问题实际上比您要描述的要复杂:您不仅在寻找一种方法来确保一个将来的完成时间晚于另一个,而且总的来说,一系列任意事件以特定顺序发生。例如,您问题中的摘录,即验证第一个完成之后的第二个开始(我不知道delay的含义是顺便说一句)。

您可以使用eventually等待特定事件发生,然后继续:

   val f = Future(doSomething) 
   eventually { 
      someFlag shouldBe true
   }
   val f1 = Future(doSomethingElse) 
   eventually { 
      f.isCompleted shouldBe true
   }
   someFlag = false   
   eventually { 
      someFlag shouldBe true
   }

   f1.futureValue shoudlBe false