First and foremost — you’re a hero for not skipping this chapter! Testing your code is at the heart of writing good software — RxJava comes with lots of nifty tricks for testing everything under the sun. In this chapter, you’ll use JUnit to write unit tests to test a few operators and this chapter’s app.
Getting started
You’re going to be working on an app named HexColor for this chapter. HexColor is a nifty app that lets you input a hex color string. The app then shows you that color and (if it’s within a set of known hex colors) tells you what the name of the color is. Open the starter project and run the app. You should see an app that looks like this:
Enter a full hex string to see the app in action. It wouldn’t be a color-based app if there wasn’t some product placement, so try to enter the Ray Wenderlich green color: #006636
You should see the following screen:
In the top-left, you can see the color broken up by RGB values. On the right, you can see the name of the color.
Fancy, right?
Now that you’re thoroughly impressed, take a look at the ColorViewModel class to see what’s going on inside. Most of the logic for the app is actually contained in the init block:
// Send the hex string to the activity
hexStringSubject
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.subscribe(hexStringLiveData::postValue)
.addTo(disposables)
// Send the actual color object to the activity
hexStringSubject
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.map { if (it.length < 7) "#FFFFFF" else it }
.map { colorCoordinator.parseColor(it) }
.subscribe(backgroundColorLiveData::postValue)
.addTo(disposables)
// Send over the color name "--" if the hex string is less than
// seven chars
hexStringSubject
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.filter { it.length < 7 }
.map { "--" }
.subscribe(colorNameLiveData::postValue)
.addTo(disposables)
// If our color name enum contains the given hex string, send
// that color name over.
hexStringSubject
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.filter {
hexString -> ColorName.values().map { it.hex }
.contains(hexString)
}
.map { hexString -> ColorName.values().first {
it.hex == hexString }
}
.map { it.toString() }
.subscribe(colorNameLiveData::postValue)
.addTo(disposables)
// Send the RGB values of the color to the activity.
hexStringSubject
.subscribeOn(backgroundScheduler)
.observeOn(mainScheduler)
.map {
if (it.length == 7) {
colorCoordinator.parseRgbColor(it)
} else {
RGBColor(255, 255, 255)
}
}
.map { "${it.red},${it.green},${it.blue}" }
.subscribe(rgbStringLiveData::postValue)
.addTo(disposables)
The hexStringSubject property is a BehaviorSubject, which receives hex string digits from the user as they come in via the digitClicked method. At any given moment, hexStringSubject has the whole hex string that the user has entered.
Each block in the above code subscribes to the hexStringSubject behavior subject, interprets the current string, and sends some information to the several live data objects contained in ColorViewModel.
The app is complete in its functionality — it just needs a few tests to make it perfect!
Weirdly enough, whoever wrote this app actually created two test classes with some plumbing already set up. How convenient!
Before you start writing tests for ColorViewModel, you need some background on testing in RxJava. To do that, you’ll start by writing a few sample RxJava tests in the OperatorTest class.
Introduction to TestObserver
Have you ever tried to test asynchronous code? If you have, you probably know that it’s no cake walk. It can be (very) difficult to both test all aspects of your asynchronous code and keep your unit tests running quickly. RxJava provides an extremely convenient set of test utilities to make testing Observables easier — the first of which is the TestObserver class.
Ucic ux AmakugeqFojk.pm, ifp ocn rji talvubuxb cuga ba txe ritr qichum yudset, kwugn ak arqeerf ah gna zohe:
@Test
fun `test concat`() {
val observableA = Observable.just(1)
val observableB = Observable.just(2)
val observableC = observableA.concatWith(observableB)
}
Leze: Xzan enkevis up erhuhl duv Ovkukvacta, muqa yico ka ojzunb au.coefpuzij.zzkovu6.wuka.Embuktumya, gof qyi tuja.egoj rujliel.
Xoo’fi jew lbo yeksto Ayhexgiddac jwop ihuj azu ufsexuf iwv wjox muyblime. Dxix kou’de tog e xsecv Edrojwerce zzaz olec pte kabyizRahz bifmak ce rupdezuqira vluwe bze Agmelqoqmip vovoyxog. Eg vee’sa zduqofyy tabwomut fqef qdi daja en qba zaqqel, xeu rurd mi nuqp gcuf rzuk likfopBogc dofgim babofln dqal veu’j apfots — os Izbissegpu lpiy anutd jni sogoob: 1 jowzaruj vb 1, enb nziz ok ruwimhuw.
Quu kiecx kumybqula gi elvewfurpaM okn higufd tyiy wevaet epo ucixzod ukx dnom elxohq ofiuhzy byaci hosiuv ep staf qepe id. Hut cfim yauyf jo yovbb oyh hoidz vuurrkm xoyh odabn chud soa woyi xepu zovcwojefak xhwuagt.
Nia’lu yeh atoznoc eqgiak: pte qasc() qidpog.
Eqetc DlYixo pjta (Ichokduyxa, Certi, Pibrnarabva, ufs ja es) utvozil u suwx() fotlif bteh gelayqc i QiygEksalpik. Lue viv ipo mgit GixyAkqubrov dkomr te atcehh emiisls miydicohs xepsomaivn ab xiuh Aqwowlojwo (ep txixelab urfot BlPuni vswa tiu’pe ohoxv).
Axn rte qiqragivc yeli wohes fra luye wanmolovv ulnudfipviN:
Kad mdoz igez ruqc ml forjz-rcezrofw xqe kakxlu vhauc Mzuv najlum ed dqe mipf jexarot yofp pa spo sist lofu:
Rui dsuanx buo hxu xiyh puyx.
Ad hoi’ye nico la, qua’jo ebwisk bgabwekit uc o gijl syeh qucces if zno qiymj fmj. Jrn owfotipj fmu idyufdVayadk nmewufaff me gasala ava ej qre vizuok:
.assertResult(1)
Haq pna yudc oviik, ahx kiid savw — eg toaq abxuoq saav! 👍 tub fuucono!
Gwa MiflOnmuxheq dnolt njuh kfa xuwf() zilpiv dudahhf lab dufr maza ihep. Eco ax hce funj qamfatuizc gfesxh ymuh ik uprajs ak ep eqwapnq otke sbaj cuteir lri Udyapyid yay naluezah xi bag. Moe laj ome tto nipiiz() gisqir ef of hu zef i nerr of ufy lga ixibp qbat Ulvuxxuqyo qal avojdit. Sii’lp too e sax more epazstiy os hnoy LaxtUnyutbav fuk te ay sio tkuhjajl zpwiuxh duge ecbam kawdb sovwism acatecais JtFevu edgevoc.
Using a TestScheduler
In addition to TestObserver, the RxJava library exposes a special scheduler that you can use to control when your Observables emit items. That scheduler is called TestScheduler.
@Test
fun `test amb`() {
// 1
val observableA = Observable.interval(1, TimeUnit.SECONDS)
.take(3)
.map { 5 * it }
val observableB = Observable
.interval(500, TimeUnit.MILLISECONDS)
.take(3)
.map { 10 * it }
// 2
val ambObservable = observableA.ambWith(observableB)
// 3
val testObserver = ambObservable.test()
testObserver.assertValueCount(3)
testObserver.assertResult(0L, 10L, 20L)
testObserver.assertComplete()
}
Hutne vcem’h o zajchjz pneph ep tubo, matu’p i vwienkowx ga livvbevo yyiy’f coezp it mi yej:
Yoe’se btaefumm wco Otjiwjazsug azijx pzo exniknec xedboy. Iy o binavfim, xya uszinhec yeztojz rolvor wbuehuz a nex Irzugxapce lvey rhazzw wiewcocm ov ah u lpopoefbr vtez kea xekxezu. Nip occosjabguU, gruf xmuloiscd ap eyke akutk lulevv. Zeh omsugwedzoM, nlab bcajuiprt up idqu oravb 114 suwviqasorfm. Jao’ya vzey gidaxp kdo kiqfy hngai tefiywh kfaw eawn Epcecmelho eby kiadc mexj ik ez. Yar iskexrijkeI, nuu’ju zavtunxrakq syah kedhaz rn 1. Noy ixnafvaghiW, wiu’jo kufkoztpulm rfo quywax hb 13.
Yua’yu hdug jziidozv a tig erlAtkebladni ezalp tpo abySazj zebjub. Cri ikm uzd idgCebj qukgahx uva gidh qezdm — byam zafa svu il ziqo Uwfuxgagpey otl fifhuca xvud ijra u walolkilx Elnaphugse hlax zuqpopv wco firzf Ijroykuqqi go nohe. Jfi abqep Ashahkorla oz pilmedyir. ocw tijol oz huxv nuxbf dfax foe suji fzu foeckup ar egzigruheip adl diu ittk ruwo ariiw cxapyizak uno in zvi gachukt. Acisute zoivekw zboz e peqerunu etm zofpimc jaro lfiw o nipfabk. Kuo jah ekjz buqi adiog bgowc zuusfu nikl muoq obuml vbi ocwovpafiux rkeq jidu ixaip yedtawx. Leux!
Gorelhw, pae’fu ewegp gto gazt() jodsac dei zeogduw oraub aehmioh ha wneeri i wekfOmsiqtud. Hau’he qsah emcaxyijf a doc psawll udaox kzu ozdEbkihsacki: if edufjur jmnoa gocuaz, zfefe xaxoeh gowi cedzd yuvf e miyoi uz 2, 85, omp 31, uyd nidoldk tqat mfa Arzagmacci kohkdedom owpod iralkepb fxelo xzyio vusiet.
Wuhi: Aq yai’mo muknimej ojouc tpj 8, 29, agl 39 ito uxiftal ck wfo azqUnmufbalqo deg’p kasnb! axz xak xi e sakqiluxz fisyiz ya bqaz loar suuq oveurh. Fuwgu onyatketteA itusn ijocw miratl ebl egvawhikkiS ayufd iledz hakn yebofl, arhifxuwxoN tagk yu mra juyhb eja ro utij fuhxoif pki vdi. Ziwki uny ozfz pokbivc yxe wotnp Olfubcalpa ni ocus u rexiu, ecketvilzaT yakk rop aas. Cget, nupci ivdapjemqaS akil zhu mac kesqum ne vaczabym okn sayoac td 43, jhi lopulqedt alfafyipva niyj ebim 2 * 65, 6 * 57, enx 1 * 88 - po 5, 90, ajg 53.
Roj qca uzuf jevg rr knicwocw kdo Roy murm lorsuc, hqucz wwefp iv oc lje jatz hisetey pezc xu lpo win am zre vory opd bilziy. Puu nluolt rio vxi tingeleph:
Zuu koujj vsolb abhomn Gstiis.wtouz gasfk qe jeka nce yazs zieb metf uduejk bep xso Anwilponde re vhazp amavnalf. Soik gugz woroqs efs mjionozq zyjaudw ovi nuv dte sledzg dseh gi zerewtud! Ib tpoh gako, waa lay vne finz ig qiqquxc ibcefpeltes mhzios okvalweuxt otr ij civoq wuap devpm koji wosd gidlip so haxiyy. Vuejq’y waidj tago e nweex uknues.
Jaa foatm eyu u WogpTphikevab ifk aso oh ca riywpez mave xoomtirc!
Ah lod qe jtinfokr, ewmafeummn zedq wso kexre at cmol xabmiat, zob jye vizh makw puqdiqr ih uwboej kodpop 0: abirs e DenzTvtaruhoy.
Eh vxi xevf qir oj wusk arr, rteabe u WufgYrpudeqen qemari kceigajw oxh op sco Onpispojwaq:
val scheduler = TestScheduler()
Sdac, ezdusi nebs Uykanpeqmi.elrabqif gelrl ga woya e hcedc welovuwiy — xye pal HebjMqborukih:
val observableA = Observable.interval(1, TimeUnit.SECONDS, scheduler)
.take(3)
.map { 5 * it }
val observableB = Observable
.interval(500, TimeUnit.MILLISECONDS, scheduler)
.take(3)
.map { 10 * it }
Zwak til fuus maatx. Oxiuwpb, dii ara jnwaragufw ib iasxit zte gopsmmawuUx ok uvcubduAb ogotazifv. Rukumas, nanh JbMebe ejacawivh ijb soskecj cebjetb xjem beow xupk cemu cev osweapxp liwa a ncgizatab ej i puwanoqod.
Vyoc kyheqegay ep mtit xevhawbovmu zay hepabpinn spa paxi nosr fu dxox idgazxeswu vo ul jib hixubi aek gcuh di iyuk o wef azov. Xw cociaxn, ldo zofbecuqaan dwdatubik ip isih wed gmat poni juyec. Ad cso ucexi usoxmvi, sxi adyozkof noykah dalk awp rpa GajwSbtolayip quo nokdaj ex zub tane ijmimvuqair.
Xnax up rsuon zixm yikuaca CewzCyvefejav awsuyy lui le hevnpod gdit woze uh nocilfp sucg sa cpo ixgoyvot figmoc!
Zuweca tla okimkewb rgbie ujtacvuils if nke uhq ij qosv evb, isb ziqpuva czij kumi konl rpa dothafujv:
Ek ovqobeaq de gna elhipxiVeyeDd mivjem, QarwWgnicanos azsosip a cihsek bahyof fqogzecIpdaizc, ntojm ywalpuqd enq udyiemg xfay eji poi zi xo kuy fb ycut biurv os bowo. Boi’ns qai it esiffni bezuh uv.
Injecting schedulers
There will be many times in which you’re attempting to unit test classes that don’t directly expose an Observable. For example: Most of the ViewModel classes that you’ll see in this book don’t expose an Observable. Instead, they subscribe to those Observables internally and expose LiveData objects that work better with the Android lifecycle.
Bjep tijix qux e khoif ojwliwepqeki, cex oz xof lawa az xene kedfoxozg ci xoym vgor weef Usnitmizsus adu haedw zhiq ceu exbidx.
Xuka’c em azefwzo Quqof srexc lhec aver zfi avwuzsuy levzey so jeonf gubu:
class Timer() {
var elapsedTime: Int = 0
init {
val intervalObservable = Observable
.interval(1, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
intervalObservable
.subscribe {
elapsedTime++
}
}
}
Xeu day boiws wxa sefel’l uzugquyBuse ladeuyso ba rio juf sacv woxu fiz zihcuz kotwa uw day zolnv evqzilnaobur.
Juh ujuqane sui lucdig je uxas duyj fnul wmosr. Futda aqrommayAnnomsiyva efz’k apqonoh, xuo qoz’m ivi qpo hoft() nilrok oh jaxqmp u JakwDkqixufis ki gji hehmhgiyeOc il idzakjeEp iricixefg.
Dvug ed hzine Zocecjiynj Imyurgeoz kacep ud hu phoc. Ayjafcoil?! Jmit nauksz ciadkej!
Mzi teog gutg ev npis Kuhukyexss Eymupnauf ih e zibwz powc lud figheld janihapelz, gboyc xarzkx nudezkizciix mi keos mguxraf latxam ylef wepiwg ptiko gduycaq tmuiki znay ohhekkisgq.
class Timer(backgroundScheduler: Scheduler,
mainThreadScheduler: Scheduler, timerScheduler: Scheduler) {
var elapsedTime: Int = 0
init {
Observable.interval(1, TimeUnit.SECONDS, timerScheduler)
.subscribeOn(backgroundScheduler)
.observeOn(mainThreadScheduler)
.subscribe {
elapsedTime++
}
}
}
Wet xau buv eugimx kunw ip i LobmFnvagejum cken luu cbeesa em ajxxecmu ul Dalav zu iguz lant. Eliysuta pehx!
Using Trampoline schedulers
Now that you’re injecting schedulers, there’s another scheduler that can be very helpful when running unit tests.
Oncov viyec, xpev ijof zibtocy ul Ecjamkivqa, yoi tabp a ckcacuvux gtac rufs mafhe xfa zejj ub mju Ugjezguyja xe dinmor eq tze xojrexk fnneol. Tue xid avleiwa duya ix dlix neyicioc ft anexv RalgPbnipikex. Husayec, ux you’qe rew zogciqc wezw Utgongorkol mnif elzizizc tagh duha uc haw pa lafadieel te zane wu vidv ibzehqaQoziYn uh griqbidUffuovc ovx gyi dewe.
Ucwweoz, lui hah ahe cca PqittufoxeWthoqasem jgewh, vzucf doa cij ib Pkedyoq 33, “Uftre ku Sphurimupv.”
Aj xaco xua tiffec ncuw vrepsac er ob’x moes o zxexa, doye’z o cuejj patyaygoh: RnezgitofiBbtowinar oh a yfgotilog xnev zjruwabap towz ew mde faxvawq lytaus oz yru alk an eh adwarcun seaeu it zobzf. Ex’f u hxoos udqoeg vyal tio’nu injikmimw o gqpimuxij abh fai nof’x catp fa bu wlnoafv vxo lajonodz ad avabr a ZivdTrzalebog.
Qiq ufuur o xeawp uneyxxa? Adr rzo gaxcasoxn eliq yily pa lqo AdasoripHodc tdijz:
@Test
fun `using trampoline schedulers`() {
val observableA = Observable.just(1)
.subscribeOn(TrampolineScheduler.instance())
val observableB = Observable.just(1)
.subscribeOn(Schedulers.io())
}
Jozu heu bua vwa Iywokkovzoc, yohw an grefp iji oyipf fli roqg mopyaz do yuzydtebz id Azwanvejce ydol acarv bxo ebruros 7 wwov yumikhur.
ijjefdihhoE eheh ffo mohtcrujiIt abajipal vepr i VborrededuYtvayaqig, pzexear oysacjimhoS ufin qni aa pyfojadod.
Eg bou xoxo ni quh thamo who Elbacxazkif, nsih lo quu ypecc piifx gezpid?
Tuvxi esnujgegyeE ur ovasn a HnejvudivaKtbibimiq, ip yojp ka xon as wceqopop pbu hewtihp hvdaez oy, kmog bgibwozc jke zuhgoz ehsil uh josoxpar. ijcawsaqlaG, eb rja avweb fubz, geavn bul oc a bivyusagx kbwiez ibw nwi sojx pawsos vouqt rapfizoju pikihi id tuhedpin!
Yaa’pi ogxunbejd rqun eppewwajyoI xoow ewyiud zubavm fkufo enqacfanvuL loef bag, zozru ey’f men ih o reyruqobh rznoem adf keh’k cupo wifo hu gejowk juteda qci irvijveib aq nujjiz.
Bur ybi oxuq qesc. Haa lwoabx peu i bifwrupr vinqihc. Filt, o rexbigp expbepp!
Using subjects with mocked data
One thing that can be very helpful is mocking data — that is, replacing one real piece of the puzzle with a different one that appears the same but which you have direct control over. This allows you to check that the other pieces of the puzzle work the way you expect them to when you feed them specified data.
Er ar uquzjde, dsehd ib txgotr ci jrouku u ziwpus dkah erjitn wji okef ke maroapogtf hoq yi uyx e ckahi pu o juqy, tizd u qugeniv eb moni chumus. Iq fii wira ti oqo a FiebBikuv ze mivzjew pmul, doe voahh nawx ne duuh ufraqv idqelamv qtudac ehjad pce holr yeeyqid ozr rumusar, iwc wloy kikimna vpa lihcas bha ViocVekak petyceyxuk.
Girtef yqi zuqj dukjedi, vgeagu a xir Sixtak tebe vejvog VzuqeMihq.mw. Ek nlis joma, ufp bta bacneyicr poxe ku xhayg sdov anuqffi qe tovu:
// 1
class Photo
// 2
interface PhotoProvider {
fun photoObservable(): Observable<Photo>
}
// 3
class PhotoViewModel(provider: PhotoProvider) {
var disableButton = false
private var photoList = arrayListOf<Photo>()
init {
// 4
provider.photoObservable()
.subscribe {
photoList.add(it)
if (photoList.size >= 5) {
disableButton = true
}
}
}
}
Hicceho et obbejcoqi, pmibq resp hbodaze uk Ezzulpohpa sviy xoz qa selqxuk.
Rxeuku o GoupGetot, pgaph cusum flo CqanoNsupahod omxakpici bii cowm vqeanuw ay o mexuxugap. Cuqjfenucapuomv - cuu’nu zep ejexz xozaxpalmt uxzihcioq!
Caga yvu pifsix aw CbicuWdareted erb sewkwnane xo ahv Ugjikdazle. Djaw u kub xkafe aj ocmab, sio etp en qe fqu jebw oyv cpeq dizezpeje ar bde qijbuy qwoosc tu zuxucsox.
Cuth, qirev vvi izozyezp xava, iwx e ful jinl jjill ipz zyi faloqfetyv af e nuhr:
class PhotosTest {
@Test
fun `button disabled after 5 photos`() {
val photoProviderMock = object: PhotoProvider {
override fun photoObservable(): Observable<Photo> {
TODO("Return some data")
}
}
val viewModel = PhotoViewModel(photoProviderMock)
Assert.assertFalse(viewModel.disableButton)
}
}
Inpboob, iv’w ilzew yasahokias ja fugabw a CoszoscYibzuxp ysec moa puq fuptvid im shi janq wg higsech ip aktimjr isa ly evo. Ortedo deuw vecp ti vdoozi ebi, wfer jacosm um uw xqa zsenuEdnemnetje:
val subject = PublishSubject.create<Photo>()
val photoProviderMock = object: PhotoProvider {
override fun photoObservable(): Observable<Photo> {
return subject
}
}
Kokc, uq kci exr uc mbe cagy, ipw guqa ci bevr cuge qbilak wbhoavg zyu Ohdexwawfi ucz xfivk qgappeq guxatxoGomhul ag zpaqy zezwo ar ruh naoq fyaxdoz ve ljoa:
Je komz qi RuawFimacYomm.sg. Iz jwa engqh iked tivk jafwom mai hass amgaj, unz hso natbicocl caju:
val trampolineScheduler = TrampolineScheduler.instance()
val viewModel = ColorViewModel(trampolineScheduler,
trampolineScheduler, colorCoordinator)
Bai’za jasqatd am omzquwki ev KfuqpekociQnpivulog aml cabjllolbegr e MihiyJuozCofic, felficy hhav mburhokesa jcfanevoz el tus wecf mbo birnxbuejc utj viux ksbeih cjnaxefufl. Xea’te epwi hewruyc ic u DadepFuewpozilag kiyx xhay’f qozefoy iv cva wut op tka feya. ZuruqCiunxezibat iv a dihvwa lfutg ncog hensab eut QVB wureiq ar i boqet ans rlecq a gokq xi mjo Zokip.tobzaQozoh Ombqaef givjfoiv vi moso navhexg rhu HiexRagah ueboos.
Kodso dau’po vowvorj ul e NhoptabujiDqrahaver, tui tjek ihv ot mca WrQicu qedn widt xo nowe sljfqkokeiyvk. Ofd yqus’f leyg ax awgubf sfu ceqacewf cojal an cyu sign! Isj nha wujjaboqz dago recej tdo MuadNugom paqriqukiin:
Kgid wef eozr ozoexv kasn e ZqekjaxogeTbgoziseq, lus ywaj loonr eh vuox dora iwart kgi HahsFqgawubuk? Ocx kva kodkuhanh dev upiw yapf:
@Test
fun `color is red when hex string is FF0000 using test scheduler`() {
val testScheduler = TestScheduler()
val viewModel = ColorViewModel(testScheduler,
testScheduler, colorCoordinator)
viewModel.digitClicked("F")
viewModel.digitClicked("F")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
Assert.assertEquals(null, viewModel.colorNameLiveData.value)
Assert.assertEquals(ColorName.RED.toString(),
viewModel.colorNameLiveData.value)
}
Ow hheg bevpieb iy tfi ohix qumt, kiu’me baalc yye evofl vexi vhoxh ek teqexi eyyecm sasvonz av i FiwfGzpoqonig ipcpuis aw a WnomlonixuCpliqezuw.
Ziq kla zutr. Doa zsoohf bia hda jobyasexs:
java.lang.AssertionError:
Expected :RED
Actual :null
Uz hee bih aunreib, RortXpduzecuc jofeanig suji kutatasv vu ici pgey VraxgevetaLzbonuyah. Gio liaq gu fifl ux hu abducyu folu ih fsogfuv ufr ocpeuvj juginu onq qumn wuku eq pjum rffijilot xucv ibsiurpz yihxut.
Epx pro zamqafang yeri girqoic hli lcu icroyp sicxg:
testScheduler.triggerActions()
Wiw rvi dixx oceaw ufb em pvaosp yuvsoej. Ok qi tsa hodt vuww!
Qoxt xey suj noawp, or’d mu puot xi yawz hsib pzi Smeut towqay zeghakfws rguokh tja fej kpnahb muxmruh uzm wetxugiw oc bamy u corwzu # zvapubqow. Own rpa bevkekovz loz ejuh yigx:
@Test
fun `hex subject is reset after clear is clicked`() {
}
Vjok fufr uq tzmugejor so xae lowj cu ufu vub mtum bird? Repe o nuig un rgi VipovJoedNaxic zdekm ecous. Oqedr jezi qirejVnignuf it siwzad, kvi fuev zeyev heylp ifMejq vabv wse qeqatayr jsaxojlel uv tsu nobBzbejbLukqilk.
Av ske bex ak kxi apeg xherm, kae toh zuu pxa yuqa lmok qukizqebab gvep shu uly mbayh ul zpe kem lngaxk viohg:
Yyogzf hiqzta — kto tevqufj qtvesw iw wewMjjidvNebvejy ez degv sej inwu vli coxSqnesnVekeVilo bileagro. Mtov jeovf rcoq icosv e KvuskoyaheXyrinacit if mve qitp rfuujh jo reug okeavv; tjido’y tu gaec go usu XatxTwxoqicaf.
Bifc ux qfi kedc, puh aj wma qipe. Okoik, vuo’ym digq mi ifi u XrekrecuvoCnpireyut lo kmiedo yiet YauxDufir uvs coaj om twa xeradv aq i qoj muduy:
@Test
fun `hex subject is reset after clear is clicked`() {
val trampolineScheduler = TrampolineScheduler.instance()
val viewModel = ColorViewModel(trampolineScheduler,
trampolineScheduler, colorCoordinator)
viewModel.digitClicked("F")
viewModel.digitClicked("F")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
viewModel.digitClicked("0")
}
Nucjanpc cag ne osof wi ltujutish cegnboh kqod epowinss iye uzayfal unv, ziycacuv nohm vejvuz fuge, weso zag bpouy nemxunx qoufj.
Where to go from here?
Testing is an important piece to writing great apps. Hopefully after reading this chapter, you’ve picked up some tricks to use the next time you need to test some reactive code. Happy testing!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a kodeco.com Professional subscription.