在 RxSwift 中测试 ViewModel

Testing ViewModel in RxSwift

我想在我的一个 ViewModel 中执行测试,其中包含一个名为 "nearByCity" 的 BehaviorRelay 对象,它绑定到名为 "isNearBy" 的 BehaviorRelay。这就是我的视图模型的样子。

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({[=10=] ?? ""})
            .map({[=10=] == ""})
            .bind(to: isNearBy)
            .disposed(by: disposeBag)

    }

}

我想做的测试是实际验证当字符串被接受时,bool值也会根据函数setupBinders()发生变化。

有什么想法吗?

谢谢

这是一种测试方法:

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 {[=11=] ?? ""}
        .map {[=11=] == ""}
}