从Futures / Iterables测试rx-observables

时间:2018-01-13 11:01:13

标签: scala rx-java reactive-programming scalatest rx-scala

我有:

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的情况下这样做?

1 个答案:

答案 0 :(得分:1)

我认为问题在于您使用AsyncWordSpecLike(顺便说一下AsyncWordSpecLike代替AsyncWordSpec?)。 AsyncWordSpecLike / AsyncWordSpec旨在简化测试Future。不幸的是,Observable是一种更强大的抽象,无法轻松映射到Future

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

我不确定在Scala上测试Observable的官方方法是什么。在Java中,我认为TestSubscriber是建议的工具。正如我所说Observable基本上比Future更强大,所以我认为要测试Observable你应该避免使用AsyncWordSpecLike / AsyncWordSpec。如果您切换为使用FlatSpecWordSpec,则可以执行以下操作:

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)
    }

  }

}