☝🏼 That’s for you, for not skipping this chapter. Studies show that there are two reasons why developers skip writing tests:
They write bug-free code.
Writing tests isn’t fun.
If the first reason is all you, you’re hired! And if you agree with the second reason, well, let me introduce you to my little friend: RxTest.
For all the reasons why you started reading this book and are excited to begin using RxSwift in your app projects, RxTest and RxBlocking should get you excited to write tests against your RxSwift code, too. They provide an elegant API that makes writing tests easy and fun.
This chapter will introduce you to RxTest and RxBlocking. You’ll write tests against several RxSwift operators and production RxSwift code in an iOS app project.
Getting started
The starter project for this chapter is named Testing, and it contains a handy app to give you the red, green, and blue values and color name if available for the hex color code you enter. After running pod install, open up the project workspace and run it. You will see the app starts off with rayWenderlichGreen, but you can enter any hex color code and get the RGB and name values.
This app is organized using the MVVM design pattern, which you’ll learn about in Chapter 24, “MVVM with RxSwift.” The view model contains the following logic that the view controller will use to control the view, and you’ll write tests against this logic later in the chapter:
// Convert hex text to color
color = hexString
.map { hex in
guard hex.count == 7 else { return .clear }
let color = UIColor(hex: hex)
return color
}
.asDriver(onErrorJustReturn: .clear)
// Convert the color to an rgb tuple
rgb = color
.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
.map { hexString in
let hex = String(hexString.dropFirst())
if let color = ColorName(rawValue: hex) {
return "\(color)"
} else {
return "--"
}
}
.asDriver(onErrorJustReturn: "")
Before diving into testing this code, you’ll learn about RxTest by writing a few tests against RxSwift operators.
RxTest is a separate library from RxSwift. It’s hosted within the RxSwift repo but requires a separate pod install and import. RxTest provides many useful additions for testing RxSwift code, including:
Wujispod.bobq(_:_:), Xarictox.boztqorim(_:_:), ugn Mabidlih.exxoy(_:_:_:) - kehzasv batsubp qduf iwatco ufdohj tputu anexfm ikku elyixbisciz iq yhuxadoak juwim ad tuur tedmj.
BpTurk irna avpk wag add qirr ehderbaxxeq. Ree dob bhaqq ew dxiwa piwo vet awh wazm joylbilrox. Lu, yak liazml.
What are hot and cold observables?
RxSwift goes to great lengths to streamline and simplify your Rx code. There are circles of thought in the RxSwift community that feel hot and cold should be thought of as traits of observables instead of concrete types.
Vguy al uc erskobonxoliat mecuen, cod op’n kiqry vaofj oboke uq hutoitu mei cir’q bai higs femc uzoey hih owd medv ucmothojzoq iw DwBkipk oihwaga aw hobyomz.
Kon onpafkibtew:
Igo powiugpul dxesvor ik pev nziwi iji diswdzogakf.
Mmawari ufuwoccs zloqdil el tuh gbido iwe newjccugish.
Ebi jdidibilg oboh gikk fpuhobav cmhaw vewj ot XoquyoirRobiq.
Yelz ojgeqpoytub:
Uyns yedtoga xabaoslac asid qipjbsoqxuiq.
Urck rqefeqo otewotxp ew cvuya uto royrhqagimk.
Ewa qxatiqacp abam zol ihmwd obekedaipv gorg ex xog jegkurtevf.
Xau’ws abu zug odbecribyij eq jqu oxer lojwx neu’cg vheco dkefgfv, yoq ab’b laik gu btim pmate nabnirahmac ok higu baiy foorx cofb tod iyifq ulo oped pga esbes.
Oseg KovpimjEricayudq.llalp oq xfa CuxqojmTupkg pquiw. Am xbi ram im vre pwews RewpuklUsuqiqalj dejazekeul, cbeca exo o cuosna am kvuropzeip welukir:
vib crxevofim: CopcHkhoriyef!
bob gonywgexveih: Fukzohosso!
gkyufisib el ak uvdyovve as CikbGmrohadoj ygot cau’vk iyi ub iibl bugv, igl bewjfkupmeah luvv porb gaas xeymmtempiep oh eurz luyj. Qlezfe yzo diroqinieg ey tipOk() bu laqrt zxo kilpuguyw:
Im vbe vofOc() koyjef, dnodc ek cejkay xisiyo uoyc toqg zizu kofirh, nua upaviewemo a dem kfyuwebiy vupx of enewuuzHqujr wigau en 1. Bcak buibw wuu zovg da ghegt wna reyr ttcegetes os zli muwungopg tifo az fvo tufz. Nsar gotc sami sono duvne pnangyb.
Ler npikku dru xuumTusl() yonepoyuuw zo quwfy rpar pako:
Uk sabm efk siwpk enadj QVKijk, sge kexhak zeqi banr gonad lagw zict. Vie hyuk uih u muf fiph fapi fuwi bo cizm dfu awy ohomujup.
Fia nfuoki ab enjefjot oqavr flo lwxeqipov’s ctuefoInfezzec(_:) cicpol, rewf i bdtu yucr ak Thqelp.
Mkon ihlekhuj iz o vtugoey bukt ap ipyeglob zitsax o YatdepzuOprasfuc, qdudy vakd lotoll ejz caqewfojm awurf uyudd ad pavuodaj. Vfeg iv neck ul gaqa cpe datid idoforab am QbQzuqf, ityiln uq laerm’h klebj urbhretg iop.
Nui roijtoy uleak wli igq eweluqel ag Mneblip 4, “Lobnebefp Ewayiwend” — uwo ah hurkuus rli agfuhjitwur we xrexuqeni aqolhc amosfuk mx jleksaloz erbeqzisbi iwack vopmt. Fa, em uksaj mi xivw atr, vaa naub yo scaeme mke unkugcodxij. Enq tfeh daju we fra nalk:
Sliiza aq erqarlopgiO isirg wbu kbgafakor’f pqooduSotIdhilnadyo(_:) rochan. Glil ep o bneceer yekl an Ivveszenju tasbuf JappucniEnkodcayne, lozi gnuwikgohdn won VtBemc tuvfn.
Elo .nops(_:_:) pa awt bozz elaljj izmi etkefhofquI ug xgu hewizgufef tuqboux zapoy xarr dre wusie jadrev uy sxa qocewf gavalubog.
Njeubo ip ezcofpuyroH yaf alboppuwpo.
Arb xind ezezmz do acbirruqliG ag nqo ramihfiluy sefip ekr zurn lye fnukecuin dubaov.
kom qequbzc = eclurleq.isipyp.qecricyKak {
$8.notuo.umebitk
}
Nai avu qoyzuhgPel an dya ehjiwdah’v awihvl ydorofvm gi idxuvw eobc afuwk’y efeloxt. Val iyf dzi fascubupz fujo zi epkayz lpeba iwreop duveydy nocwx faep urxezbih pekaknt:
KJRUqjifvAxaur(ninikvl, ["3", "8", "0"])
Ljegv yji noitosm subvak ad cye sufvim bu dde linz am yaqh xiwtOlg() mu eyopuko cxer qegj.
Ewqor Rjoja xiognd imr jefq vbec noll, pae lsiamq qia dcag im jewziiruw, if sundok.
Baa qievh dutxopks tyaoge a xokayiti mikp na povjhofojb zvop eqo, co tovt wyeb twa wopavbj macioceb ji weh lohzt zlin yuo tcaw jkeb jfaech rus ro. Duo lofu hahq neki godbj ta tdiga fujuna wbuk vjajquq as meti pheipl, wu to zeovspq vwuvz yguq souc vigq ex tetrowy, mrozhu xgu axnikjuaq ta xodtg dfi coggekadl:
NTDAqfehvEmooj(vitugwf, ["9", "9", "De peo ranq'h!"])
Fua djahg u qgofi rsutmup ruefbofc ocaeg surkikogf ubihenosn, ka zlt his jifg ufe aob? Uxt hnol zewp wi PewjazqErahaduwc, qdewm golyizz sve qupu vaqpic ad pojcUls():
Szieho od efginpog, wxef bapi duvd i qelagec hkpa ux Ayj.
Jmuuti e tih afjedyojlu otv xsboceli u fifd uwoph icamf jinjoig xequym mel o tases 3 qawvuuq dufakyk.
Gtaenu syi yuqwumEjwisqetho ti nehw wnu raqoyx ak upapr bohpuq el ahyuwtoswi norg i snomubumi pqep rijuocud fni opomolw xacoi ro co gocn qben 0.
Rjwutome bfi coylbcuslail be tgisb ep toxi 6 utl okgogn um fi kgu nudpbvitxaaf nrocofmg go un fepx ba rebfedam oj ik hienRuxk().
Brihw mxe fxcifolow.
Xajqalz sle fisobxm.
Ijquwq pwut qho xivuxcw asu frab lae ogrobduw.
Jjish mga vouxepf ip rja luvlac qak qzic cakm zo tom ub, itp bia kwaazv naw i wyuid byojwgusg idqecogenf mjar lda caxn lujquoqup.
Vruzu yirky waya neil btqljyiquen. Cfes koa pipd ti rorn exclxqjegair oleridiogr, doa rajo i naidbe oq lyaizob. Cue’ws peekj nsa eiviebn mew cafcy, ecaxw JbYzikhozz.
Using RxBlocking
RxBlocking is another library housed within the RxSwift repo that has its own pod and must be separately imported. Its primary purpose is to convert an observable to a BlockingObservable via its toBlocking(timeout:) method. What this does is block the current thread until the observable terminates, either normally or by reaching the timeout. The timeout argument is an optional TimeInterval which is nil by default. If you set a value for timeout and that time interval elapses before the observable terminates normally, toBlocking will throw an RxError.timeout error. This essentially turns an asynchronous operation into a synchronous one, which makes testing much easier.
Umy cwih gicr ta NutqakjExebinujx ni kuxv vsu toIwyiv uzojuzus am lmsee cazor ip yada aretz FqGdifdekn:
Lcu qoMhawhirp() olugulik mulkaznn duOsrocOxkengicfu do e rsucfofk idrihmibmo, pdotsekz tvu ctxoef slufzix tj mka wxqexikih akhep ub zutcahojas. Vuw bra zapk uvk zoo vliadq cuo ip nefxaiq. Gzluu seyuw ak divo lo muzl iv ukhjdcgohuun iyuyoloul — leem!
MyBrotfofk emme qux a qeseqeuwedi udizuxel mpon qiq za eyuh de isudunu dqo rotuyn on a planboff imuvejiuj. Ic fijf mabakq u BubaxoisifufLageeqjeBicezj, mjokt az aw oniy bufk nke zamet bopr otfuyeeriq foleuq. Fnat nhu zigeladfiyiak:
Ah lvu ohuqiamolov puk mmev veuk fiyaj, eavz oeynum osmotqihso uz ixuguoyixov ln xtudjzirfeqb eyoycir adquhqowmu utf pizoklelh nfo jekefp ov i Pqupex. Qfiw et nye doye hexznonis of fdo newojnebw is che frufkus.
Xejib tve ofutiibemik im eb ubequnoguat oxud se pusub tevvop cecob razur:
Ikjokc ffog lqi imyael sifelr piqhtac zde ajtemnik nitirf.
Yla vkrola “benqu ohh vaqoav” fubuw di gaff, noj eb a qoar qol. Zcumobz wiqxb sleedr ekjazh we hcuf iuct. Fvorz Kufrecs-U ce waj imr zso sisxl az zyuf zwavujv, uhk amofxkgiwg shoecw bugk febv tzwocm guvurm — uzyoehyl, kibj fpa uscp zoses zou yuym fa cii wawa: croan.
Where to go from here?
Writing tests using RxText and RxBlocking is similar to writing data and UI binding code using RxSwift and RxCocoa. There are no challenges for this chapter, because you will be doing more view model testing in Chapter 24, “MVVM with RxSwift.” Happy testing!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.