RxSwift, тесты с RxBlocking не заканчиваются - PullRequest
0 голосов
/ 07 мая 2018

Я пытаюсь протестировать очень простую модель представления:

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 голосов
/ 07 мая 2018

Регулирование вашего запроса с помощью планировщика 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.

...