我正在尝试在编写的脚本中测试错误处理。如果异步函数fetchBar失败,则我将模式匹配失败情况,然后返回包含失败结果的成功Future。
val fetchedBar = Try(fooClient.fetchBar(params))
fetchedBar match {
case Success(bar) => foobar(bar)
case Failure(e) => Future.successful(FooResult(success = false))
}
但是,当我对该流进行单元测试时,我在测试失败案例时遇到了麻烦。我已将fetchBar存根以返回失败的将来,如下所示。
val fetchedBar = Try(Future.failed(new Exception()))
但是我注意到fetchedBar返回的是Success而不是Failure。为什么会这样,如何将fetchBar函数存根以创建失败的Try?
答案 0 :(得分:2)
我认为您在混搭一些概念-但这不是100%的错。
问题是,Scala中的Future
是一个有点不正交的概念-它不仅代表延迟执行的概念,而且代表失败的概念。
因此,在大多数情况下,将“将来”包装到“尝试”中是没有多大意义的,反之亦然-除非人们想将失败的概念与异步的概念明确地分开。
换句话说,以下组合有些奇怪,但仍然有其用途:
Try[Future[_]]
-未来已经捕获了失败。但是,如果您有一个(行为不佳的)库方法,该方法通常返回Future,但可能会抛出“同步”路径,则是有道理的:def futureReciprocal(i: Int): Float = {
val reciprocal = 1 / i // Division by zero is intentional
Future.successful(reciprocal)
}
futureReciprocal(0) // throws
Try(futureReciprocal(0)) // Failure(DivisionByZero(...))
...但是基本上,这是一种解决方法,可解决功能未实现的问题
Future[Try[_]]
-有时对于将“业务”错误(由Future.success(Failure(...))
表示)与“基础结构”故障(由Future.failed(...)
表示)有用。一方面-这对于akka流特别有用,它会将失败的期货视为对流“致命”。对于您而言,您想要做的是根据将来的结果断言。为此,实际上您至少有两个选择。
scala.concurrent.Await
:// writing this without the compiler, might mix up namespaces a bit
import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
val future = fooClient.fetchBar(...)
val futureResult: Try[_] = Await.result(future, 1.second)
futureResult match { case Success(_) => ??? ; case Failure(exc) => ???; }
class YourTest extends FlatSpec with ScalaFutures {
"fetchBar should return failed future" in {
val future: Future[XYZ] = fooClient.fetchBar(...)
// whenReady comes from the ScalaFutures trait
whenReady(future) { result => result shouldBe XYZ } // asserting on the successful future result
whenReady(future.failed) { exc => exc shoulBe a[RuntimeException] } // asserting on an exception in the failed future
}
}