昨天講了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,但最終還是沒嘗試出來,若有大大會的話,也請指教一下,就這樣,明天見!