RxSwift_v1.0筆記——16 Testing with RxTest
100分
以上這是給你的袍榆,為了表?yè)P(yáng)你沒(méi)有略過(guò)此章節(jié)。研究表明開發(fā)者略過(guò)編寫測(cè)試用例有兩個(gè)原因:
- 他們只會(huì)寫沒(méi)有錯(cuò)誤的代碼
- 編寫測(cè)試用例不好玩
如果你只是第一個(gè)原因丸相,那么你被錄用了!如果你也同意第二個(gè)原因,那么讓我給你介紹一下我的小朋友:RxTest妨退》砘担基于之所以你開始閱讀這本書并很激動(dòng)的將RxSwift用于你的APP項(xiàng)目中的所有原因锭魔,RxTest(和RxBlocking)也會(huì)很快讓你對(duì)用RxSwift 代碼 編寫測(cè)試用例感興趣。它們會(huì)提供一個(gè)簡(jiǎn)潔的API路呜,讓編寫測(cè)試用例變得簡(jiǎn)單而有趣迷捧。
這個(gè)章節(jié)將會(huì)給你介紹RxTest织咧,稍后是RxBlocking,用來(lái)寫測(cè)試
本章將向您介紹RxTest以及RxBlocking漠秋,通過(guò)針對(duì)多個(gè)RxSwift操作編寫測(cè)試笙蒙,并針對(duì)RxSwift產(chǎn)品代碼編寫測(cè)試。
開始 300
這個(gè)章節(jié)的啟動(dòng)設(shè)計(jì)名字叫Testing庆锦,它包含一個(gè)掌上APP捅位,可以為輸入的16進(jìn)制顏色代碼提供紅,綠搂抒,藍(lán)色值和顏色名字(若有)艇搀。運(yùn)行安裝后,打開這個(gè)workspace并運(yùn)行求晶。你可以看到這個(gè)APP用 rayWenderlichGreen開始焰雕,但是你可以輸入任意16進(jìn)制顏色代碼并獲得rgb和顏色名字。
這個(gè)APP是使用MVVM設(shè)計(jì)模式組織起來(lái)的芳杏,你可以在MVVM章節(jié)學(xué)習(xí)MVVM的相關(guān)知識(shí)矩屁。簡(jiǎn)單來(lái)說(shuō)就是邏輯代碼被封裝在視圖模型中,視圖控制器用來(lái)控制視圖爵赵。除了枚舉流行的顏色名稱之外档插,整個(gè)應(yīng)用程序都運(yùn)行在這個(gè)邏輯上,您將在本章稍后部分中寫出測(cè)試:
// Convert hex text to color
color = hexString.asObservable()
.map { hex in
guard hex.characters.count == 7 else { return .clear }
let color = UIColor(hex: hex)
return color
}
.asDriver(onErrorJustReturn: .clear)
// Convert the color to an rgb tuple
rgb = color.asObservable()
.map { color in
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
color.getRed(&red, green: &green, blue: &blue, alpha: nil)
let rgb = (Int(red * 255.0), Int(green * 255.0), Int(blue * 255.0))
return rgb
}
.asDriver(onErrorJustReturn: (0, 0, 0))
// Convert the hex text to a matching name
colorName = hexString.asObservable()
.map { hexString in
let hex = String(hexString.characters.dropFirst())
if let color = ColorName(rawValue: hex) {
return "\(color)"
} else {
return "--"
}
}
.asDriver(onErrorJustReturn: "")
在投入這個(gè)代碼到testing之前亚再,編寫兩個(gè)針對(duì)RxSwift操作的測(cè)試用例對(duì)學(xué)習(xí)RxTest 是很用幫助的郭膛。
Note:這個(gè)章節(jié)是假設(shè)你很熟悉在iOS系統(tǒng)中用XCTest編寫單元測(cè)試,如果你是新手氛悬,可以找下我們的視頻課程(原失效)https://www.raywenderlich.com/150521/updated-course-ios-unit-ui-testing
用RxTest測(cè)試操作 301
Note:因?yàn)镾wift包管理的問(wèn)題则剃,原“RxTests”已經(jīng)重命名為RxTest。因此如果你在野外(out in the wild)看到了“RxTests”如捅,它很可能是指RxTest棍现。
RxTest是RxSwift的獨(dú)立庫(kù)。 它在RxSwift repo內(nèi)托管(host)镜遣,但需要單獨(dú)的pod安裝和導(dǎo)入己肮。 RxTest為測(cè)試RxSwift代碼提供了許多有用的補(bǔ)充,例如TestScheduler悲关,它是一個(gè)虛擬時(shí)間scheduler谎僻,可以精確控制測(cè)試時(shí)間線性操作,包括 next(::)寓辱, completed(::)艘绍,和 error(::),可以在測(cè)試中的指定時(shí)間將這些事件添加到observables秫筏。 它還添加了冷和熱observables诱鞠,你可以把它想象成冷熱三明治挎挖。不,不是真的航夺。
什么的是熱和冷的observables蕉朵? 301
RxSwift用了大量的篇幅去簡(jiǎn)化你的Rx代碼,并且他們有辦法讓你明白熱的和冷的區(qū)別阳掐,當(dāng)談到observables始衅,在RxSwift里更多的考慮的是observables的特點(diǎn)是而不是具體類型。這有點(diǎn)像一點(diǎn)補(bǔ)充的細(xì)節(jié)锚烦,但是它值得你多加關(guān)注,因?yàn)樵赗xSwift 的測(cè)試內(nèi)容以外是沒(méi)有這么多討論熱的和冷的observable的帝雇。
熱observables:
- 使用資源是否有訂閱者涮俄。
- 產(chǎn)生元素是否有訂閱者。
- 主要用于狀態(tài)類型尸闸,如Variable彻亲。
冷observables:
- 僅僅在訂閱時(shí)消耗資源
- 有訂閱者才產(chǎn)生元素
- 主要使用異步操作,例如網(wǎng)絡(luò)吮廉。
你稍后寫的單元測(cè)試將使用熱observables苞尝。 但是,如果您需要使用另一個(gè)需求宦芦,請(qǐng)了解不同之處宙址。
打開在TestingTests組中的TestingOperators.swift。在類 TestingOperators的頂部定義了兩個(gè)屬性:
var scheduler: TestScheduler!
var subscription: Disposable!
scheduler是 TestScheduler的一個(gè)實(shí)例调卑,你將使用在每個(gè)test中抡砂,并且 subscription將保持你每個(gè)test中的訂閱。改變setUP()的定義:
override func setUp() {
super.setUp()
scheduler = TestScheduler(initialClock: 0)
}
在setUP()方法中恬涧,在每個(gè)測(cè)試用例開始都會(huì)調(diào)用它注益。你用TestScheduler (initialClock: 0)初始化一個(gè)新的scheduler。它的意思是你希望在測(cè)試開始時(shí)啟動(dòng)測(cè)試 scheduler溯捆。這很快就會(huì)變得有意義丑搔。
現(xiàn)在改變 tearDown()的定義:
override func tearDown() {
scheduler.scheduleAt(1000) {
self.subscription.dispose()
}
super.tearDown()
}
tearDown()在每個(gè)測(cè)試完成時(shí)調(diào)用。在它里面提揍,在1000毫秒后你調(diào)度測(cè)試訂閱的銷毀啤月。你寫的每個(gè)測(cè)試將運(yùn)行至少1秒,因此在1秒后銷毀測(cè)試的訂閱是安全的劳跃。
現(xiàn)在朋友顽冶,是時(shí)候?qū)憸y(cè)試了。在 tearDown()的定義后面增加一個(gè)新的test到TestingOperators:
//1
func testAmb() {
//2
let observer = scheduler.createObserver(String.self)
}
你做了以下內(nèi)容:
- 像所有使用XCTest的tests一樣售碳,方法名必須以test開頭强重。你建立了一個(gè)名叫amb的測(cè)試绞呈。
- 你使用scheduler的 createObserver(_:)方法與String類型的示意創(chuàng)建了一個(gè)觀察者
觀察者將記錄它接收到的每個(gè)事件的時(shí)間戳,就像在RxSwift中的debug操作间景,但不會(huì)打印任何輸出佃声。在Combining Operators章節(jié)你已經(jīng)學(xué)習(xí)了amb操作。amb被用在兩個(gè)observables之間倘要,哪個(gè)observable首先發(fā)射圾亏,它就只傳播它發(fā)射的事件。你需要?jiǎng)?chuàng)建兩個(gè)observables封拧。增加下面代碼到test:
//1
let observableA = scheduler.createHotObservable([
// 2
next(100, "a)"),
next(200, "b)"),
next(300, "c)")
])
// 3
let observableB = scheduler.createHotObservable([
// 4
next(90, "1)"),
next(200, "2)"),
next(300, "3)")
])
這個(gè)代碼做了:
- 使用 scheduler的createHotObservable(_:)創(chuàng)建一個(gè)observableA志鹃。
- 使用next(::)方法在指定的時(shí)間(毫秒)添加.next事件到observableA上 ,第二個(gè)參數(shù)作為值傳遞泽西。
- 創(chuàng)建 名為observableB的熱observable
- 用規(guī)定的值在指定的時(shí)間增加 .next事件到 observableB
要知道amb將只傳播第一個(gè)發(fā)射事件的observable的事件曹铃。你能夠猜到這個(gè)這個(gè)測(cè)試就是為了測(cè)這個(gè)。
為了測(cè)試這個(gè)捧杉,增加下面的代碼來(lái)使用amb操作并分配結(jié)果到一個(gè)本地常量:
let ambObservable = observableA.amb(observableB)
Option-click在ambObservable上陕见,你將看到它是 Observable<String>類型。
Note:如果你的Xcode又出了毛病(on the fritz)味抖,你可能會(huì)看到<<error type>>评甜,不要擔(dān)心,運(yùn)行測(cè)試時(shí)Xcode會(huì)識(shí)別它仔涩。
下一步忍坷,你需要告訴scheduler來(lái)調(diào)度在指定時(shí)間的動(dòng)作。增加下面代碼:
scheduler.scheduleAt(0) {
self.subscription = ambObservable.subscribe(observer)
}
這里你調(diào)度了 ambObservable在0時(shí)訂閱到observer熔脂,并分配訂閱到 subscription屬性承匣。這樣一來(lái),tearDown()將銷毀訂閱锤悄。
為了確實(shí)地開始(kick off)測(cè)試然后確認(rèn)結(jié)果韧骗,增加下面代碼:
scheduler.start()
這將啟動(dòng)虛擬時(shí)間調(diào)度程序,并且觀察者將收到您通過(guò)amb操作指定的.next事件零聚。
現(xiàn)在你能夠收集和分析結(jié)果袍暴。輸入以下代碼:
let results = observer.events.map {
$0.value.element!
}
在觀察者的事件屬性上你使用map訪問(wèn)每個(gè)事件的元素。現(xiàn)在你能斷言這些實(shí)際的結(jié)果通過(guò)增加下面代碼來(lái)匹配你期望的結(jié)果
XCTAssertEqual(results, ["1)", "2)", "3)"])
點(diǎn)擊函數(shù) testAmb()左側(cè)溝槽(gutter)中的鉆石按鈕來(lái)執(zhí)行測(cè)試隶症。
當(dāng)測(cè)試結(jié)束后政模,你應(yīng)該看到完成了(又叫(aka)通過(guò))
通常你將創(chuàng)建一個(gè)負(fù)面測(cè)試來(lái)補(bǔ)充這個(gè),例如測(cè)試接收到的結(jié)果與你知道的他們應(yīng)該不是這個(gè)的結(jié)果不一致蚂会。這章節(jié)完成之前你還有更多的測(cè)試要寫淋样,因此要快速測(cè)試你的測(cè)試是否工作,按以下內(nèi)容更改斷言:
XCTAssertEqual(results, ["1)", "2)", "No you didn't!"])
再次運(yùn)行測(cè)試確保出現(xiàn)以下錯(cuò)誤信息:
XCTAssertEqual failed: ("["1)", "2)", "3)"]") is not equal to ("["1)", "2)", "No you didn't!"]")
撤銷上面的改變?cè)龠\(yùn)行測(cè)試確保它再次通過(guò)胁住。
你花了一整章節(jié)來(lái)學(xué)習(xí)過(guò)濾操作趁猴,為什么不測(cè)試一個(gè)呢刊咳?增加下面的測(cè)試到 TestingOperators,它與 testAmb()保持了一樣的格式:
func testFilter() {
// 1
let observer = scheduler.createObserver(Int.self)
// 2
let observable = scheduler.createHotObservable([
next(100, 1),
next(200, 2),
next(300, 3),
next(400, 2),
next(500, 1)
])
// 3
let filterObservable = observable.filter {
$0 < 3
}
// 4
scheduler.scheduleAt(0) {
self.subscription = filterObservable.subscribe(observer)
}
// 5
scheduler.start()
// 6
let results = observer.events.map {
$0.value.element!
}
// 7
XCTAssertEqual(results, [1, 2, 2, 1])
}
從頭開始:
- 創(chuàng)建一個(gè)觀察者儡司,時(shí)間類型為Int娱挨。
- 創(chuàng)建一個(gè)熱observable,它每秒schedulers一個(gè).next事件捕犬,共5秒跷坝。
- 創(chuàng)建 filterObservable來(lái)保存在observable上使用過(guò)濾的結(jié)果,過(guò)濾條件為判斷元素的值小于3碉碉。
- 在0時(shí)開始調(diào)度訂閱并分配它到訂閱屬性以便它將在 tearDown()被銷毀柴钻。
- 啟動(dòng)scheduler。
- 收集結(jié)果垢粮。
- 斷言你期望的結(jié)果贴届。
點(diǎn)擊這個(gè)測(cè)試旁溝槽的鉆石圖標(biāo)運(yùn)行測(cè)試,你將得到綠勾指示了測(cè)試成功足丢。
這些測(cè)試已經(jīng)同步粱腻。當(dāng)你想測(cè)試異步操作庇配,你有兩個(gè)選擇斩跌。你將首先學(xué)習(xí)容易的一個(gè),使用RxBlocking捞慌。
使用RxBlocking 306
RxBlocking是封裝(housed)在RxSwift repo內(nèi)部的另一個(gè)庫(kù)耀鸦,像RxTest一樣,有它自己的pod且必須分開導(dǎo)入啸澡。它的主要目的是通過(guò)它的 toBlocking(timeout:)方法袖订,轉(zhuǎn)換一個(gè)observable到 BlockingObservable。這樣做會(huì)阻塞當(dāng)前線程嗅虏,直到observable終止洛姑,或者如果指定了一個(gè)超時(shí)值(默認(rèn)情況下為零),并且在observable終止之前達(dá)到超時(shí)皮服,則會(huì)引發(fā)RxError.timeout錯(cuò)誤楞艾。 這基本上將異步操作轉(zhuǎn)換為同步操作,使測(cè)試變得更加容易龄广。
增加下面在RxBlocking內(nèi)的三行測(cè)試代碼到 TestingOperators來(lái)測(cè)試 toArray操作:
func testToArray() {
// 1
let scheduler = ConcurrentDispatchQueueScheduler(qos: .default)
// 2
let toArrayObservable = Observable.of("1)",
"2)").subscribeOn(scheduler)
// 3
XCTAssertEqual(try! toArrayObservable.toBlocking().toArray(), ["1)",
"2)"])
}
它做了的如下:
- 使用默認(rèn)的服務(wù)質(zhì)量硫眯,創(chuàng)建并發(fā)scheduler來(lái)運(yùn)行異步測(cè)試
- 創(chuàng)建observable來(lái)保持在scheduler上,訂閱到兩個(gè)字符串的observable的結(jié)果择同。
- 對(duì)toArrayObservable調(diào)用toBlocking()的結(jié)果使用toArray两入,并斷言toArray的返回值等于預(yù)期結(jié)果。
toBlocking()轉(zhuǎn)換 toArrayObservable為一個(gè)阻塞observable敲才,阻止由scheduler產(chǎn)生的線程裹纳,直到它終止择葡。運(yùn)行測(cè)試你應(yīng)該看到成功。僅用三行代碼就測(cè)試了一個(gè)異步操作——哇痊夭!你將用簡(jiǎn)潔的RxBlocking做更多工作刁岸,但現(xiàn)在是時(shí)候離開操作的測(cè)試并寫一些針對(duì)(against)應(yīng)用產(chǎn)品代碼的測(cè)試。
測(cè)試RxSwift的產(chǎn)品代碼 307
首先打開在Testing組中的ViewModel.swift她我。在頂部虹曙,你將看到一些屬性定義:
let hexString = Variable<String>("")
let color: Driver<UIColor>
let rgb: Driver<(Int, Int, Int)>
let colorName: Driver<String>
hexString接收來(lái)至視圖控制器的輸入。color番舆,rgb和colorName是輸出酝碳,視圖控制器將綁定到視圖。在視圖模型的初始中恨狈,通過(guò)轉(zhuǎn)換另一個(gè)observable并把返回結(jié)果作為Driver疏哗。這是顯示在章節(jié)開始處的代碼。
接下來(lái)初始化的是一個(gè)枚舉類型禾怠,定義到模型的常見的顏色名返奉。
enum ColorName: String {
case aliceBlue = "F0F8FF"
case antiqueWhite = "FAEBD7"
case aqua = "0080FF"
// And many more...
現(xiàn)在打開ViewController.swift,聚焦到 viewDidLoad()的實(shí)現(xiàn)上吗氏。
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
guard let textField = self.hexTextField else { return }
textField.rx.text.orEmpty
.bindTo(viewModel.hexString)
.disposed(by: disposeBag)
for button in buttons {
button.rx.tap
.bindNext {
var shouldUpdate = false
switch button.titleLabel!.text! {
case "?":
textField.text = "#"
shouldUpdate = true
case "←" where textField.text!.characters.count > 1:
textField.text = String(textField.text!.characters.dropLast())
shouldUpdate = true
case "←":
break
case _ where textField.text!.characters.count < 7:
textField.text!.append(button.titleLabel!.text!)
shouldUpdate = true
default:
break
}
if shouldUpdate {
textField.sendActions(for: .valueChanged)
}
}
.disposed(by: self.disposeBag)
}
viewModel.color
.drive(onNext: { [unowned self] color in
UIView.animate(withDuration: 0.2) {
self.view.backgroundColor = color
}
})
.disposed(by: disposeBag)
viewModel.rgb
.map { "\($0.0), \($0.1), \($0.2)" }
.drive(rgbTextField.rx.text)
.disposed(by: disposeBag)
viewModel.colorName
.drive(colorNameTextField.rx.text)
.disposed(by: disposeBag)
}
從頭開始:
- 綁定文本框的文本(或者一個(gè)空的字符串)到視圖模型的hexString輸入observable
- 循環(huán)遍歷按鈕出口的集合芽偏,綁定tap并轉(zhuǎn)換按鈕的標(biāo)題來(lái)決定怎樣更新文本框的文字,與文本框是否應(yīng)該發(fā)送valueChanged控制事件弦讽。
- 使用視圖模型的color驅(qū)動(dòng)來(lái)更新視圖的背景顏色污尉。
- 使用視圖模型的rgb驅(qū)動(dòng)來(lái)更新rbgTextField的文本。
- 使用實(shí)體模型的coloName驅(qū)動(dòng)來(lái)更新colorNameTextField的文本往产。
通過(guò)預(yù)覽app是如何工作的被碗,你現(xiàn)在能夠針對(duì)它來(lái)寫測(cè)試。在TestingTests組內(nèi)打開TestingViewModel.swift仿村,按如下修改setUP()的實(shí)現(xiàn):
override func setUp() {
super.setUp()
viewModel = ViewModel()
scheduler = ConcurrentDispatchQueueScheduler(qos: .default)
}
這里锐朴,你分配app ViewModel類的一個(gè)實(shí)體給viewModel屬性,用默認(rèn)服務(wù)質(zhì)量的一個(gè)并發(fā)scheduler給scheduler屬性蔼囊。
現(xiàn)在你可以開始針對(duì)app的視圖模型來(lái)寫測(cè)試了焚志。首先,你將使用傳統(tǒng)的XCTest API編寫一個(gè)異步測(cè)試压真。增加視圖模型顏色驅(qū)動(dòng)(使用傳統(tǒng)方式)的測(cè)試到TestingViewModel:
func testColorIsRedWhenHexStringIsFF0000_async() {
let disposeBag = DisposeBag()
// 1
let expect = expectation(description: #function)
// 2
let expectedColor = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha:
1.0)
// 3
var result: UIColor!
}
你做了以下工作:
- 創(chuàng)建一個(gè)稍后實(shí)現(xiàn)的預(yù)期娩嚼。
- 創(chuàng)建 expectedColor等于紅色的預(yù)期的測(cè)試結(jié)果。
- 定義結(jié)果稍后分配滴肿。
這僅僅是起始代碼≡牢颍現(xiàn)在將以下代碼添加到測(cè)試以訂閱視圖模型的color驅(qū)動(dòng)程序:
// 1
viewModel.color.asObservable()
.skip(1)
.subscribe(onNext: {
// 2
result = $0
expect.fulfill()
})
.disposed(by: disposeBag)
// 3
viewModel.hexString.value = "#ff0000"
// 4
waitForExpectations(timeout: 1.0) { error in
guard error == nil else {
XCTFail(error!.localizedDescription)
return
}
// 5
XCTAssertEqual(expectedColor, result)
}
- 創(chuàng)建一個(gè)訂閱到視圖模型的color驅(qū)動(dòng)。注意你略過(guò)了第一個(gè)元素,因?yàn)轵?qū)動(dòng)將在訂閱上重放初始元素贵少。
- 分配.next事件元素到result并在expect上調(diào)用fulfill()呵俏。
- 在視圖模型的hexString上增加一個(gè)新的值輸入給observable(一個(gè)Variable)。
- 用1秒來(lái)超時(shí)等待expectation的完成滔灶,并在閉包中為error提供guard
- 斷言期望的color等于實(shí)際的result普碎。
很簡(jiǎn)單但有點(diǎn)冗長(zhǎng)。運(yùn)行測(cè)試確保它通過(guò)录平。
現(xiàn)在使用RxBlocking來(lái)實(shí)現(xiàn)同樣的事情:
func testColorIsRedWhenHexStringIsFF0000() {
// 1
let colorObservable =
viewModel.color.asObservable().subscribeOn(scheduler)
// 2
viewModel.hexString.value = "#ff0000"
// 3
do {
guard let result = try colorObservable.toBlocking(timeout:
1.0).first() else { return }
XCTAssertEqual(result, .red)
} catch {
print(error)
}
}
- 創(chuàng)建coloObservable來(lái)保存訂閱在并發(fā)scheduler上的observable結(jié)果麻车。
- 在視圖模型的hexString上增加一個(gè)新值輸入給observable。
- 使用guard來(lái)選擇將調(diào)用toBlocking()的結(jié)果與1秒的超時(shí)綁定斗这,如果拋出动猬,捕獲并打印錯(cuò)誤,然后斷言實(shí)際的結(jié)果與預(yù)期的匹配表箭。
運(yùn)行測(cè)試確保它是成功的赁咙。這個(gè)測(cè)試本質(zhì)上與前一個(gè)相同。你只是不需要那么辛苦免钻。
接下來(lái)彼水,添加此代碼以測(cè)試視圖模型的rgb驅(qū)動(dòng)為給定的hexString輸入發(fā)出預(yù)期的紅色,綠色和藍(lán)色值:
func testRgbIs010WhenHexStringIs00FF00() {
// 1
let rgbObservable =
viewModel.rgb.asObservable().subscribeOn(scheduler)
// 2
viewModel.hexString.value = "#00ff00"
// 3
let result = try! rgbObservable.toBlocking().first()!
XCTAssertEqual(0 * 255, result.0)
XCTAssertEqual(1 * 255, result.1)
XCTAssertEqual(0 * 255, result.2)
}
- 創(chuàng)建rgbObservable來(lái)保存在scheduler上的訂閱极舔。
- 在視圖模型的hexString上增加一個(gè)新值輸入給observable凤覆。
- 檢索在rgbObservable上調(diào)用toBlocking的第一個(gè)結(jié)果,然后斷言每個(gè)值與期望的匹配姆怪。
01轉(zhuǎn)換到0255僅僅是為了匹配測(cè)試名并讓接下來(lái)的事情更加容易叛赚。運(yùn)行這個(gè)測(cè)試確保它成功通過(guò)澡绩。
還有一個(gè)要測(cè)試的驅(qū)動(dòng)程序 將此測(cè)試添加到TestingViewModel稽揭,來(lái)測(cè)試視圖模型的colorName驅(qū)動(dòng)為給定的hexString輸入發(fā)出正確的元素:
func testColorNameIsRayWenderlichGreenWhenHexStringIs006636() {
// 1
let colorNameObservable =
viewModel.colorName.asObservable().subscribeOn(scheduler)
// 2
viewModel.hexString.value = "#006636"
// 3
XCTAssertEqual("rayWenderlichGreen", try!
colorNameObservable.toBlocking().first()!)
}
- 創(chuàng)建observable
- 增加測(cè)試值。
- 斷言實(shí)際的結(jié)果來(lái)匹配期望的結(jié)果肥卡。
這是我想起了短語(yǔ)”漂洗和重復(fù)“溪掀,這是一個(gè)好的方式。寫測(cè)試就是應(yīng)該簡(jiǎn)單步鉴。按Command-U運(yùn)行在項(xiàng)目中的所有測(cè)試揪胃,所有測(cè)試都應(yīng)該通過(guò)。
使用RxText and RxBlocking寫測(cè)試是使用RxSWift和RxCocoa寫數(shù)據(jù)和UI綁定(以及其他)氛琢。這章沒(méi)有挑戰(zhàn)喊递,因?yàn)槟銓⒃贛VVM章中做更多的視圖模型測(cè)試。測(cè)試真高興阳似!