0

我有:

val observable: Observable[Int] = Observable.from(List(5))

我可以通过测试测试输入列表是否确实传递给了 observable:

materializeValues(observable) should contain (5)

哪里materializeValues是:

def materializeValues[T](observable: Observable[T]): List[T] = {
  observable.toBlocking.toIterable.toList
}

现在,如果我从未来创建一个 observable,由于测试超时,我似乎无法materializeValues用于测试。所以如果我有:

val futVal = Future.successful(5)
val observable: Observable[Int] = Observable.from(futVal)
materializeValues(observable) should contain(5)

它超时并且没有通过测试。物化这两个可观察的过程有什么不同,导致我无法阻止它?

另外,测试可观察对象的惯用方式是什么?有什么方法可以不打电话toBlocking吗?

4

1 回答 1

1

我认为问题在于您使用AsyncWordSpecLike(顺便说一下为什么AsyncWordSpecLike而不是AsyncWordSpec?)。AsyncWordSpecLike/AsyncWordSpec旨在简化测试Future。不幸Observable的是,一个更强大的抽象不能轻易映射到Future.

特别是AsyncWordSpecLike/AsyncWordSpec允许您的测试返回Future[Assertion]。为了使它成为可能,它提供了自定义隐式ExecutionContext,它可以强制执行所有内容并知道所有计划的作业何时完成。但是,相同的习惯ExecutionContext是您的第二个代码不起作用的原因:仅在您的测试代码执行完成后才开始处理计划的作业,但您的代码阻塞在 上,futVal因为不幸的是,您注册的回调Future.onComplete计划运行在ExecutionContext. _ 这意味着您对自己的线程有一种死锁。

我不确定Observable在 Scala 上测试的官方方法是什么。在 Java 中,我认为TestSubscriber是推荐的工具。正如我所说Observable,从根本上说比Future我认为测试Observable你应该避免使用更强大的东西AsyncWordSpecLike/ AsyncWordSpec。如果您切换到使用FlatSpecor WordSpec,您可以执行以下操作:

class MyObservableTestSpec extends WordSpec with Matchers {

  import scala.concurrent.ExecutionContext.Implicits.global
  val testValue = 5

  "observables" should {

    "be testable if created from futures" in {
      val futVal = Future.successful(testValue)
      val observable = Observable.from(futVal)

      val subscriber = TestSubscriber[Int]()
      observable(subscriber)
      subscriber.awaitTerminalEvent
      // now after awaitTerminalEvent you can use various subscriber.assertXyz methods
      subscriber.assertNoErrors
      subscriber.assertValues(testValue)
      // or you can use Matchers as 
      subscriber.getOnNextEvents should contain(testValue)
    }

  }

}
于 2018-01-14T11:15:47.233 回答