RxSwift, тесты с RxBlocking не заканчиваются
Я пытаюсь проверить очень простую модель представления:
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 ответ
Ваше регулирование запроса с помощью планировщика 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
,