昨天講了RxBlocking和RxTest,今天就將兩天前所寫的範例,加上測試程式吧!
宣告
var viewModel: ProductListViewModel!
viewModel就是待會要測試的對象
測試程式的setUp()和tearDown()
override func setUp() {
super.setUp()
let apiService = TestAPI()
viewModel = ProductListViewModel(apiService: apiService)
}
override func tearDown() {
super.tearDown()
}
setUp()時,建立測試的API,並且注入到ViewModel當中,另外,初始化TestScheduler原始版本
func test_init_load_data() {
let disposeBag = DisposeBag()
// 1
let expect = expectation(description: #function)
var result: [Product]!
viewModel.data.asObservable()
.skip(1) // 2
.subscribe(onNext: {
// 3
result = $0
expect.fulfill()
})
.disposed(by: disposeBag)
// 4
waitForExpectations(timeout: 5.0) { error in
guard error == nil else {
XCTFail(error!.localizedDescription)
return
}
// 5
XCTAssertEqual(20, result.count)
}
}
expectation會建立一個XCTestExpectation,可用來測試非同步狀況,因為非同步並非由我們指定的步驟進行,所以,我們在預期的會經過的地方,放expect.fulfill(),執行到這行的話,就代表達到我們預期。BehaviorRelay,在訂閱後會拿到最一開始會得的[],所以我們skip(1)
expect.fulfill(),表示符合我們預期waitForExpectations(timeout: 5.0)來表示等待5秒,若在5秒內都沒有執行到expect.fulfill(),那測試就會失敗TestAPI()中的假資料RxBlocking版本
嘗試以RxBlocking測試ViewModel初始化
func test_init_load_data_ver_blocking() throws {
let disposeBag = DisposeBag()
// 1
let observable = viewModel.data.skip(1).map { $0.count }
// 2
viewModel.data.subscribe().disposed(by: disposeBag)
// 3
XCTAssertEqual(try observable.toBlocking().first(), 20)
}
XCTestExpectation
BehaviorRelay,所以,就算不訂閱也能發送元素,反之,若是需要訂閱才能發送的Observable,就必須寫這段first()來獲取第一個元素,預期會得到20筆func test_pull_to_refresh() throws {
let disposeBag = DisposeBag()
// 1
viewModel.data.skip(1).take(1)
.subscribe(onNext: { _ in
self.viewModel.triggerAPI.onNext(()) // 2
})
.disposed(by: disposeBag)
// 3
let observable = viewModel.data.skip(2).map { $0.count }
// 4
XCTAssertEqual(try observable.toBlocking().first(), 20)
}
skip(1)略過BehaviorRelay的初始值,使用take(1)來抓取第二個元素,也就是初始化後會call API,這時data會收到第二個元素viewModel.triggerAPI發送元素,也就會執行reloadfunc test_betch_reload() throws {
let disposeBag = DisposeBag()
viewModel.data.skip(1).take(1)
.subscribe(onNext: { _ in
self.viewModel.triggerNextPage.onNext(()) // 1
})
.disposed(by: disposeBag)
let observable = viewModel.data.skip(2).map { $0.count }
// 2
XCTAssertEqual(try observable.toBlocking().first(), 21)
}
viewModel.triggerNextPage發送元素本來嘗試在使用RxTest,但最終還是沒嘗試出來,若有大大會的話,也請指教一下,就這樣,明天見!