Тестирование ViewModel в RxSwift - PullRequest
1 голос
/ 11 июня 2019

Я хотел бы выполнить тест в одной из моих ViewModels, которая содержит объект BehaviorRelay с именем "nearByCity", который привязан к BehaviorRelay с именем "isNearBy". Вот так выглядит моя модель зрения.

class SearchViewViewModel: NSObject {

    //MARK:- Properties
    //MARK: Constants
    let disposeBag = DisposeBag()


    //MARK: Vars
    var nearByCity:BehaviorRelay<String?> = BehaviorRelay(value: nil)
    var isNearBy = BehaviorRelay(value: true)        

    //MARK:- Constructor
    init() {

        super.init()
        setupBinders()

    }

}


//MARK:- Private methods
private extension SearchViewViewModel{

    func setupBinders(){

        nearByCity
            .asObservable()
            .distinctUntilChanged()
            .map({$0 ?? ""})
            .map({$0 == ""})
            .bind(to: isNearBy)
            .disposed(by: disposeBag)

    }

}

Тест, который я хочу выполнить, состоит в том, чтобы фактически убедиться, что когда строка принята, значение bool также изменяется в соответствии с функцией setupBinders ().

Любая идея?

Спасибо

1 Ответ

1 голос
/ 11 июня 2019

Вот один из способов проверки:

class RxSandboxTests: XCTestCase {

    func testBinders() {
        let scheduler = TestScheduler(initialClock: 0)
        let source = scheduler.createColdObservable([.next(5, "hello"), .completed(10)])
        let sink = scheduler.createObserver(Bool.self)
        let disposeBag = DisposeBag()

        let viewModel = SearchViewViewModel(appLocationManager: StubManager())
        source.bind(to: viewModel.nearByCity).disposed(by: disposeBag)
        viewModel.isNearBy.bind(to: sink).disposed(by: disposeBag)

        scheduler.start()

        XCTAssertEqual(sink.events, [.next(0, true), .next(5, false)])
    }
}

Некоторые другие пункты:

  • Не заставляйте ваши предметные свойства var использовать взамен let, потому что вы не хотите, чтобы кто-либо мог заменить их несвязанными версиями.

  • Тот факт, что вы должны использовать AppLocationManager в этом коде, который вам не нужен, подразумевает, что объект делает слишком много. Нет ничего плохого в наличии нескольких моделей представлений в контроллере представлений, каждая из которых обрабатывает разные части представления.

  • Лучше всего вообще избегать использования объектов (реле) в коде модели представления, при необходимости их лучше оставлять в императивной части кода.

Как минимум, разбейте функцию setupBinders, чтобы детали можно было проверить независимо. Выше можно было написать как простую, легко тестируемую бесплатную функцию:

func isNearBy(city: Observable<String?>) -> Observable<Bool> {
    return city
        .distinctUntilChanged()
        .map {$0 ?? ""}
        .map {$0 == ""}
}
...