代码之家  ›  专栏  ›  技术社区  ›  emenegro

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

  •  1
  • emenegro  · 技术社区  · 6 年前

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

    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)
    }
    

    但这永远不会结束。

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

    RxTest和RxBlocking的文档非常简短,其中有一个字符串或整数的经典示例,但与这种流无关。。。这很令人沮丧。

    1 回复  |  直到 6 年前
        1
  •  4
  •   JustinM    6 年前

    您正在使用MainScheduler限制查询。实例调度器。试着移除它,看看会发生什么。这可能就是为什么你只得到一个。测试时,需要将测试调度器注入到该节流阀中。

    有几种不同的方法可以将正确的调度器引入到您的模型中。根据您当前的代码,依赖项注入可以很好地工作。

    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

    推荐文章