RxSwift: How to Unit Test Search in the search bar and display the results as a table

So, I've been using Rxswift for a while and it worked well. I've managed to test all of my code, but I'm struggling to figure out how to test searches with searchbar.rx.bindTo.

There are many tutorials on how to use RxSwift to search and return results as a table, but none of these tutorials show you how to unit test it. https://www.thedroidsonroids.com/blog/ios/rxswift-by-examples-1-the-basics/

The link above shows what I am trying to achieve using the search bar and populate the TableView.

I've tried testing it with RxBlocking but all tests seem to hang. systemUnderTest - This viewModel result is an Observable <[T]> that is returned from the service.

let results = systemUnderTest.results.toBlocking()
    let noneObservableList = try! results.single()

    //Then
    XCTAssert(noneObservableList?.count == expectedCount)

      

He's hanging on trying! results.single () and never hits the assertion. Does anyone know how to check this.

Thanks in advance.

This is systemUnderTest:

public class SearchViewModel: SearchViewModelContract {

public var query: Variable<String?> = Variable(String.EmptyString())
public var results: Observable<[ThirdPartySite]>
let minimumCharacterCount = 4
let dueTime = 0.3
let disposeBag = DisposeBag()

public init() {
    results = Observable.just([Object]())
    results = query.asObservable().throttle(dueTime, scheduler: MainScheduler.instance).flatMapLatest{
        queryString  -> Observable<Object> in

        if let queryString = queryString {
            if queryString.characters.count >= self.minimumCharacterCount {
                return self.Something(siteName: queryString)
            }
            return Observable.just(Object(in: Object()))
        }
        return Observable.just(Object(in: Object()))
        }.map { results in
            return results.items
        }.catchErrorJustReturn([Object]()).shareReplay(1)
    }
}

      

+5


source to share


1 answer


I have several suggestions:

  • query

    and results

    should be allowed, not variables. You should never dump an observable. This is part of what it means to be functional.
  • you seem to be using an older version of RxSwift; I suggest you upgrade. - UPDATE: DOH! Of course this is an old version of RxSwift, this is an old question!
  • Unit testing code that has side effects (network call) embedded in it can be a huge PITA. Raise side effects to a higher level so you can test this without him.
  • The operator is debounce

    much better suited for data transmission than throttle

    . The latter works better for triggers.

As for your main question on how to unit test your search, I've found a lot of success with RxTest

. Here is the code to generate the SearchTerm Observable along with a test to prove it works:



extension ObservableType where Element == String? {

    func searchTerm(minCharacterCount: Int = 4, dueTime: RxTimeInterval = .milliseconds(300), scheduler: SchedulerType = MainScheduler.instance) -> Observable<String> {
        return self
            .compactMap { $0 }
            .filter { minCharacterCount <= $0.count }
            .debounce(dueTime, scheduler: scheduler)
    }
}

class Tests: XCTestCase {

    var scheduler: TestScheduler!
    var result: TestableObserver<String>!
    var disposeBag: DisposeBag!

    override func setUp() {
        super.setUp()
        scheduler = TestScheduler(initialClock: 0, resolution: 0.001)
        result = scheduler.createObserver(String.self)
        disposeBag = DisposeBag()
    }

    func testExample() {
        let input = scheduler.createColdObservable([
            .next(1000, Optional.some("")),
            .next(2000, Optional.some("xyz")),
            .next(3000, Optional.some("wxyz")),
            .next(4000, Optional.some("vwxyz")),
            .next(4300, Optional.some("uvwxyz"))
            ])

        input
            .searchTerm(scheduler: scheduler)
            .subscribe(result)
            .disposed(by: disposeBag)

        scheduler.start()

        XCTAssertEqual(result.events, [.next(3300, "wxyz"), .next(4600, "uvwxyz")])
    }
}

      

flatMapLatest

should go into your side code, stuff that you are not testing with a module.

+1


source







All Articles