RxSwift,使用 RxBlocking 的测试不会结束
RxSwift, tests with RxBlocking do not end
我正在尝试测试一个非常简单的视图模型:
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({ ![=10=].isEmpty })
.flatMapLatest({ searchService.search(query: [=10=]) })
}
}
我正在尝试测试从服务中接收错误,所以我将其加倍:
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)
}
但这永远不会结束。
我不知道我的存根是否有问题,或者 viewmodel 是否有问题,但生产应用程序正常工作,在查询触发时发出事件。
RxTest 和 RxBlocking 的文档非常非常短,带有字符串或整数的经典示例,但与这种流程无关...非常令人沮丧。
您正在使用 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({ ![=10=].isEmpty })
.flatMapLatest({ searchService.search(query: [=10=]) })
}
}
那么在你的测试中:
let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true), scheduler: testScheduler)
此外,您可以将结果事件绑定到 testable Observer
。
而不是使用 toBocking()
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
时,您会获得更多信息。
我正在尝试测试一个非常简单的视图模型:
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({ ![=10=].isEmpty })
.flatMapLatest({ searchService.search(query: [=10=]) })
}
}
我正在尝试测试从服务中接收错误,所以我将其加倍:
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)
}
但这永远不会结束。
我不知道我的存根是否有问题,或者 viewmodel 是否有问题,但生产应用程序正常工作,在查询触发时发出事件。
RxTest 和 RxBlocking 的文档非常非常短,带有字符串或整数的经典示例,但与这种流程无关...非常令人沮丧。
您正在使用 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({ ![=10=].isEmpty })
.flatMapLatest({ searchService.search(query: [=10=]) })
}
}
那么在你的测试中:
let sut = SearchViewModelImpl(searchService: SearchServiceStub(erroring: true), scheduler: testScheduler)
此外,您可以将结果事件绑定到 testable Observer
。
toBocking()
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
时,您会获得更多信息。