SickMan
SickMan

Reputation: 75

RxSwift TestScheduler don't work if subscribeOn background

I have mock interactor and router for unit tests on presenter presenter method:

private func presenterMethod(_ isOn: Bool) -> Driver<Bool> {
    return interactor.interactorMethod(isOn)
        .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
        .observeOn(MainScheduler.instance)
        .do(onError: { [weak self] error in
            self?.view.showError(error)
        })
        .asDriver(onErrorJustReturn: !isOn)
}

And test

func testPresenterMethod() {
    let trigger = self.scheduler.createHotObservable([
        next(100, (false)),
        next(200, (true)),
        next(300, (false))
        ]).asDriverOnErrorJustComplete()

    let observer = scheduler.createObserver(Bool.self)
    let input = createInput(presenterTrigger: trigger)
    let output = presenter.transform(input)

    scheduler.scheduleAt(0, action: {
        output.presenterMethodOutput.asObservable()
            .subscribe(observer)
            .disposed(by: self.disposeBag)
    })

    scheduler.start()

    let results = observer.events.map {
        $0.value.element
    }

    XCTAssertEqual(results, [false, true, false])
}

And results are empty,

This test work correctly only when I remove these lines from presenterMethod

    .subscribeOn(ConcurrentDispatchQueueScheduler(qos: .background))
    .observeOn(MainScheduler.instance)

I tried with XCTestExpectation and fulfill in do(onNext:{}) block and get the same results, work only without subscribeOn background. Method work correct on device and simulator, presenterMethod is treggered on switch and emit correct events. How this test should be written to work with SubscribeOn background?

Upvotes: 4

Views: 2088

Answers (1)

tomahh
tomahh

Reputation: 13651

You'll actually want the schedulers passed to subscribeOn to be a configurable variable on the presenter.

Because tests are better ran synchronously, passing the test scheduler in will ensure there's no need to wait for an asynchronous execution.

struct Presenter {
  let mainScheduler: SchedulerType  
  let backgroundScheduler: SchedulerType

  init(backgroundScheduler: SchedulerType = ConcurrentDispatchQueueScheduler(qos: .background), mainScheduler: SchedulerType = MainScheduler.instance) {
    self.mainScheduler = mainScheduler
    self.backgroundScheduler = backgroundScheduler
  }

  private func presenterMethod(_ isOn: Bool) -> Driver<Bool> {
    return interactor.interactorMethod(isOn)
        .subscribeOn(backgroundScheduler)
        .observeOn(mainScheduler)
        .do(onError: { [weak self] error in
            self?.view.showError(error)
        })
        .asDriver(onErrorJustReturn: !isOn)
    }
}

And then, when creating the presenter whithin the tests

let presenter = Presenter(backgroundScheduler: self.scheduler, mainScheduler: self.scheduler)

Upvotes: 3

Related Questions