RxSwift,使用RxBlocking进行的测试不会结束

时间:2018-05-07 15:11:09

标签: ios swift rx-swift rx-blocking

我试图测试一个非常简单的视图模型:

struct SearchViewModelImpl: SearchViewModel {
    let query = PublishSubject<String>()
    let results: Observable<BookResult<[Book]>>

    init(searchService: SearchService) {
        results = query
            .distinctUntilChanged()
            .throttle(0.5, scheduler: MainScheduler.instance)
            .filter({ !$0.isEmpty })
            .flatMapLatest({ searchService.search(query: $0) })
    }
}

我试图测试从服务中接收错误,所以我用这种方式加倍:

class SearchServiceStub: SearchService {
    let erroring: Bool

    init(erroring: Bool) {
        self.erroring = erroring
    }

    func search(query: String) -> Observable<BookResult<[Book]>> {
        if erroring {
            return .just(BookResult.error(SearchError.downloadError, cached: nil))
        } else {
            return books.map(BookResult.success) // Returns dummy books
        }
    }
}

我正在测试以这种方式出错的查询:

func test_when_searchBooksErrored_then_nextEventWithError() {
    let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true))
    let observer = scheduler.createObserver(BookResult<[Book]>.self)

    scheduler
        .createHotObservable([
            Recorded.next(200, ("Rx")),
            Recorded.next(800, ("RxSwift"))
        ])
        .bind(to: sut.query)
        .disposed(by: disposeBag)

    sut.results
        .subscribe(observer)
        .disposed(by: disposeBag)

    scheduler.start()

    XCTAssertEqual(observer.events.count, 2)
}

首先,我只是断言事件的数量是否正确,但我只收到一个而不是两个。我认为这是一个异步性问题所以我改变了测试以使用RxBlocking:

func test_when_searchBooksErrored_then_nextEventWithError() {
    let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true))
    let observer = scheduler.createObserver(BookResult<[Book]>.self)

    scheduler
        .createHotObservable([
            Recorded.next(200, ("Rx")),
            Recorded.next(800, ("RxSwift"))
        ])
        .bind(to: sut.query)
        .disposed(by: disposeBag)

    sut.results.debug()
        .subscribe(observer)
        .disposed(by: disposeBag)

    let events = try! sut.results.take(2).toBlocking().toArray()

    scheduler.start()

    XCTAssertEqual(events.count, 2)
}

但这永远不会结束。

我不知道我的存根或者视图模型是否有问题,但是生产应用程序正常工作,在查询触发时发出事件。

RxTest和RxBlocking的文档非常简短,使用字符串或整数的经典示例,但没有任何与此类流程相关的...这非常令人沮丧。

1 个答案:

答案 0 :(得分:3)

使用MainScheduler.instance调度程序限制查询。尝试删除它,看看会发生什么。这可能是你唯一获得一个的原因。在测试时,您需要将测试调度程序注入该限制器。

有几种不同的方法可以将正确的调度程序添加到模型中。根据您当前的代码,依赖注入可以正常工作。

struct SearchViewModelImpl: SearchViewModel {
    let query = PublishSubject<String>()
    let results: Observable<BookResult<[Book]>>

    init(searchService: SearchService, scheduler: SchedulerType = MainScheduler.instance) {
        results = query
            .distinctUntilChanged()
            .throttle(0.5, scheduler: scheduler)
            .filter({ !$0.isEmpty })
            .flatMapLatest({ searchService.search(query: $0) })
    }
}

然后在你的测试中:

let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true), scheduler: testScheduler)

此外,您可以将结果事件绑定到toBocking(),而不是使用testable Observer

func test_when_searchBooksErrored_then_nextEventWithError() {
    let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true), scheduler: testScheduler)
    let observer = scheduler.createObserver(BookResult<[Book]>.self)

    scheduler
        .createHotObservable([
            Recorded.next(200, ("Rx")),
            Recorded.next(800, ("RxSwift"))
        ])
        .bind(to: sut.query)
        .disposed(by: disposeBag)

    sut.results.bind(to: observer)
     .disposed(by: disposeBag)

    scheduler.start()

    XCTAssertEqual(observer.events.count, 2)
}

虽然toBlocking()在某些情况下非常有用,但在将事件绑定到testableObserver时会获得更多信息。