RxSwiftのテストは何かと面倒
scheduler
やdisposeBag
、obserever
など
テストしたい処理以外に書くべきことが多い。。
// ロジックは省略
func testIsTapEnabled() {
let isTapEnabled = scheduler.createObserver(Bool.self) // これとか
viewModel.output.isTapEnabled
.drive(isTapEnabled) // これとか
.disposed(by: disposeBag) // これとか
scheduler.createColdObservable([.next(10, ()),
.next(20, ()),
.next(30, ())])
.bind(to: viewModel.input.playButtonTapped)
.disposed(by: disposeBag)
scheduler.start() // これとか
XCTAssertEqual(isTapEnabled.events, [
.next(true),
.next(false),
.next(true),
.next(false)
])
}
ObservableConvertibleTypeをぶち込むだけで、
ボイラーブレートを内部でやってくれるクラスが欲しい
便利クラス
import RxSwift
import RxTest
final class Stream<Element> {
private let disposeBag = DisposeBag()
private let observer: TestableObserver<Element>
private let scheduler = TestScheduler(initialClock: 0)
// ObservableConvertibleTypeなのでDriverでもObservableでも代入可
// Driverを使用しないと決めている場合はObservableに書き換えるとよさそう
init<O: ObservableConvertibleType>(_ observable: O) where Element == O.Element {
observer = scheduler.createObserver(Element.self)
observable
.asObservable()
.subscribe(observer)
.disposed(by: disposeBag)
}
var events: [Event<Element>] {
return observer.events.compactMap { $0.value }
}
var latestEvent: Event<Element>? {
return observer.events.last.flatMap { $0.value }
}
var value: Element? {
return event?.element
}
}
これを使うと、最初に紹介したコードが次のように書けます。
let isTapEnabled = Stream(viewModel.output.isTapEnabled) // outputの監視を始める。
viewModel.input.playButtonTapped.onNext(())
viewModel.input.playButtonTapped.onNext(())
XCTAssertEqual(isTapEnabled.events, [
.next(true),
.next(false),
.next(true)
])
schedulerをStreamのinitializerに入れれば、schedulerを意識したテストもできるようになります。
(省略して逆にイベント送信が冗長になってしまいました)