【问题标题】:How to test if futures run sequentially in Scala?如何测试期货是否在Scala中按顺序运行?
【发布时间】:2020-07-30 14:51:01
【问题描述】:

假设我有这个方法:

def callApis(f1, f2, f3):Future[Result] {
    for {
        a <- Future { f1 }
        b <- Future { f2 }
        c <- Future { f3 }
    } yield Result(a,b,c)
}

如果您熟悉 scala,您就会知道 for 块中的行将按顺序执行。更具体地说,将首先计算 a。然后当我们得到 a 的结果时,代码将计算 b。然后当我们得到b的结果时,代码将计算c。

我的问题是,如何编写一个单元测试来确保始终在计算 b 之前计算 a,并且始终在计算 c 之前计算 b? 我担心如果有人不知道很多关于期货如何在scala中工作的信息。他们可能会不小心使此代码异步运行。

我的意思是人们可能不小心做这样的事情,这使得 a,b,c 被异步计算(我不希望人们这样做)

def callApis(f1, f2, f3):Future[Result] {
    val fut1 = Future { f1 }
    val fut2 = Future { f2 }
    val fut3 = Future { f3 }

    for {
        a <- fut1
        b <- fut2
        c <- fut3
    } yield Result(a,b,c)
}

【问题讨论】:

  • 如果在第一个任务完成之前提交第二个任务,则使用失败的执行上下文对其进行测试。如果期货启动更多期货,那么记账就更难了,您应该使用诸如倒计时锁存器之类的显式协调,并让他们根据需要使用第二种语法。
  • @som-snytt 我理解你的决心。但是如果第二个任务在第一个任务完成后提交,我们不能确定代码是否按顺序运行。我的意思是我们只能确定第一个任务完成后第二个任务何时开始。我不知道如何检查。
  • 我想这不是一个解决方案,因为你需要Future,但如果没有这样的限制,我建议使用任何现有的IO而不是Future(例如Cats Effect)。这将为您提供开箱即用的保证,因此您无需进行测试。
  • @highDopamine 除了您已经收到的有价值的 cmets 和答案之外,我想指出,测试来自您使用的库的设计决策的行为感觉有点奇怪我。这就像在你告诉 mockito 返回一些值时测试它是否真的返回一些值。如果您对 Futures 的这种行为方式有疑问(我很高兴您认识到了这一点),那么投资于猫 IO、Monix Task 或 ZIO 等效果类型怎么样?我知道不是每个项目都能负担得起,但也许你的可以。

标签: scala unit-testing asynchronous future


【解决方案1】:

也许尝试定义一个单线程执行上下文,并在应该串行执行的块中要求它。例如,

trait SerialExecutionContext extends ExecutionContext {
  val singleThreadPool = Executors.newFixedThreadPool(1, (r: Runnable) => new Thread(r, s"single-thread-pool"))
  val serialEc = ExecutionContext.fromExecutor(singleThreadPool)
  override def execute(runnable: Runnable): Unit = serialEc.execute(runnable)
  override def reportFailure(cause: Throwable): Unit = serialEc.reportFailure(cause)
}


def callApis()(implicit ec: SerialExecutionContext): Future[Result] = {
  val fut1 = Future { ...doSomething... }
  val fut2 = Future { ...doSomething... }
  val fut3 = Future { ...doSomething... }

  for {
    a <- fut1
    b <- fut2
    c <- fut3
  } yield Result(a,b,c)
}

现在callApis 只有在我们可以在编译时证明存在串行执行上下文时才能进行评估。由于在正文中我们只有一个线程可用,future 只能在前一个线程完成后才被强制启动。

【讨论】:

  • 感谢您的回答,但这似乎与我的问题无关。我编辑了我的问题以使其更清楚。请检查。
  • 这将是一个帮助你晚上睡觉的解决方案,除了只有一个线程的部分。我们不知道 doSomething 做了什么,或者是否还有其他任务要运行。只是想说它与问题有关,但显然需要进行扩展讨论才能确定要求。可能与其他 cmets 一样,需要不同的抽象。
猜你喜欢
  • 2019-11-04
  • 1970-01-01
  • 2010-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-08
  • 1970-01-01
  • 2015-12-21
相关资源
最近更新 更多