Тестирование rx-наблюдаемых из Futures/Iterables

Я имею:

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

и я могу проверить, что входной список действительно передается наблюдаемому, проверяя:

materializeValues(observable) should contain (5)

где materializeValues является:

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

Теперь, если я создаю наблюдаемое из будущего, я не могу использовать materializeValues для теста, поскольку тест истекает. Так что если у меня есть:

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

он истекает и не проходит тест. Что отличается в процессе материализации этих двух наблюдаемых, что приводит к тому, что я не могу блокировать это?

Кроме того, каков идиоматический способ проверки наблюдаемого? Есть ли способ сделать это без звонка toBlocking?

1 ответ

Решение

Я думаю, что проблема в том, что вы используете AsyncWordSpecLike (кстати почему AsyncWordSpecLike вместо AsyncWordSpec?). AsyncWordSpecLike/AsyncWordSpec предназначены для упрощения тестирования Future, к несчастью Observable это более мощная абстракция, которая не может быть легко отображена на Future,

в частности AsyncWordSpecLike/AsyncWordSpec позвольте вашим тестам вернуться Future[Assertion], Чтобы сделать это возможным, он предоставляет пользовательские неявные ExecutionContext что он может заставить все выполнить и знать, когда все запланированные задания завершены. Однако тот же обычай ExecutionContext причина того, что ваш второй код не работает: обработка запланированных заданий начинается только после завершения выполнения тестового кода, но ваш код блокируется на futVal потому что, к несчастью для вас, обратный звонок зарегистрирован в Future.onComplete планируется запустить на ExecutionContext, Это означает, что у вас есть своего рода тупик с вашим собственным потоком.

Я не уверен, что это официальный способ тестирования Observable на скале. В Java я думаю, что TestSubscriber является предлагаемым инструментом. Как я сказал Observable принципиально более мощная вещь, чем Future так что думаю проверить Observable вам следует избегать использования AsyncWordSpecLike/AsyncWordSpec, Если вы переключитесь на использование FlatSpec или же 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)
    }

  }

}
Другие вопросы по тегам