使用 fire and forget Futures 进行 Scala 测试

Scala testing with fire and forget Futures

所以我有一些带有端点的服务器代码,我想在其中 运行 异步填充。为此,我正在使用期货。有导致结果的任务,但也有不会导致结果的昂贵 IO 任务,所以我在火中执行它们并忘记 Future 像这样:

import scala.concurrent.Future

val ec = scala.concurrent.ExecutionContext.global

Future {
  Thread.sleep(10000)
  println("Done")
}(ec)

println("Exiting now")

输出是:

import scala.concurrent.Future

ec: scala.concurrent.ExecutionContextExecutor = scala.concurrent.impl.ExecutionContextImpl@2315ca48

res0: scala.concurrent.Future[Unit] = scala.concurrent.impl.Promise$DefaultPromise@7d1e50cb

Exiting now
res1: Unit = ()

通常在服务器上这不是什么大问题,因为上下文在接收其他任务时保持 运行ning,因此 IO 可以完成。但是当我测试的时候,测试结束,里面的任务开始抛出,因为他们使用的服务变得不可用。

所以问题是:如何在不阻塞主要执行的情况下等待这些 fire and forget futures?

为了说明我的回答,假设您访问一些 fileService 以在即发即弃部分加载文件,并且您使用 Mockito(或其他模拟框架,想法保持不变)用于你的单元测试。然后,我将在解释中提到的代码看起来像这样:

class SystemUnderTest(val fileService: MyFileService) {
  def testedMethod(): GoodResult = {
    val file = // ... some logic to extract file ...
    Future {
      fileService.uploadFile(file)
    }
    // ... other logic returning an instance of GoodResult
  }
}

然后,在你的测试中加入 org.scalatest.Eventually 并做出如下断言:

eventually {
  verify(fileService).uploadFile(any())
}

它将确保主进程在验证上传服务已被调用(或 implicit patienceConfig 定义的超时已过期)之前不会退出。


当然,您始终可以选择 return 您不在生产代码中等待的 Future 而是在测试中等待它。