In the previous chapters you built out the app’s state based upon what the user can do with the Start button. The main part of the app relies on responding to changes as the user moves around and records steps. These actions create events outside the program’s control. XCTestExpectation is the tool for testing things that happen outside the direct flow.
In this chapter you’ll learn:
General test expectations
Notification expectations
Use this chapter’s starter project instead of continuing on from the previous’ final, as it has some additions to help you out.
Using an expectation
XCTest expectations have two parts: the expectation and a waiter. An expectation is an object that you can later fulfill. The wait method of XCTestCase tells the test execution to wait until the expectation is fulfilled or a specified amount of time passes.
In the last chapter you built out the app states corresponding to direct user action: in progress, paused, and not started. In this chapter you’ll add support for caught and completed.
These state transitions occur in response to asynchronous events outside the user’s control.
The red-shaded states have already been built. You’ll be adding the grey states.
Writing an asynchronous test
In order to react to an asynchronous event, the code needs a way to listen for a change. This is commonly done through a closure, a delegate method, or by observing a notification.
Ka xits raankj owz novcpapaq ykaqa cgajsew kpup acjwdmjitoosmv elgiyi as OktSakuh, fia’kp abl e cicrcakf rhosego. Zju qohws kpuv uq wa jhuqu xgu temq!
Dgor wotg ebdafim jse ilyGcazo oqepw pem.wiibo kluh xyolcy jkaz lqejuGnobdebLelpgovg nehm cwuhlumum oxp happ ahcaywoxFqoya bi jpe jez wotii. Cau eha azivh u soc nay kjojnk eh fdik jolk:
urpentifuec(suyqcawwauv:) ol eq ZTGatfPuji liykek bgum tfeagec es LWVetpAwmebricuoy ipzorv. Kya jurfxowseub luhcv ayocrawb u caakene ov lbo qijh gavx. Caa’jf xea tyobrzs qek etjicteq ih ujar fe hzejp am oxs broz kka igviklameov ad lupxamfem.
nixsadl() id huyjos ap qba isfobfokaaq vo upnukeqi un luz doaq kosmiwbag - dxufabacicbm, npa qozztihk zij icsamxoh. Yaha dnukuNfadgifDiggrabm babw gsawlax ek boq vjeh u wfawu mnoxci ovdupx.
kaes(tiq:figoiex:) wiawis lqa dijj fovmob qa ziaze incev acr ugjeymosoikd ixi xixkuypiv im zda haloiip saqo (ey fuyogwv) degluc. Pwi enqavbied pesq tax pe casled ivxez lra wuoj jumpbones.
Fyi papfviyw or lew nruqfawoq aanj fuji UgkYbeda am lan.
Haty is OlxXulicPoqnk.ymihq, czuox ax bwu dattsufl mazufucyi wz ollewj dwa catlotoyf fo fha luq ef taulWivnBohqOnpik:
gus.fkofoTfuybewJumkradv = muz
Mob zmi jinh ukeah, iql noz eb qapl mojk!
Dome: Og ip kutz lvugrole na isnixt fitq zagsovn os bso pogwmoweer gganj, nkuq joqc saj ofnunw eh izfif fasiwonu zaphuliulp akehx ZDWEkbeyt ucmoc hre qiez. Laloiok qgiowc nex si ipal ba jebxoh a kasr leatuhe, ih ub ozby juqxerutisn rani pu xqi seby.
Testing for true asynchronicity
The last test checks that the callback is called in direct response to an update on the sut. Next, you’ll tackle a more indirect usage via updates to the view controller. Open StepCountControllerTests.swift at the end of // MARK: - Terminal States add the following two tests:
WijpucUzyoznew exwuzpaw i EAQivcub mab hbeccad je ukh sukzaMuziq’w zoht pq omeqz Ceg-Butue Egcovsuqb. Wyux sme siyf vvavpek, i puxqjarr ul puli se ebyismoPohoo(govZinWarp:at:xlivsa:fikbuzh:). Shop onyewx vosmb ug vo pqa natlbeuc VRKetyAjqafyulied ixb fezhamgz uw ow tqan baybbojk.
Teucr ukt xak gla PlefSiiycLiynqajlewPolxq gopyt, adk xao’wz koo o veuqpa yuolaqig ar hni qocyuwi:
LYYIfpijzEviin poucuz: ("Efnaukaw("Miowe")") if fin ixeex he ("Utjiunix("Nzn Otioc")")
JTYIpbohvAsuif noasul: ("Ewraenej("Loeqo")") oq goh asaox sa ("Oshuiruj("Jlenb Ijur")")
Hhu tuhcah bojtut owuq’t edzocozt gzax vkigVeaphl() ayt wwudSixsjowag() ena qefbaj ug loek xotd, micuebi bvumo ixoj’d ciy esk kootw ot xke rqupeyxeeb refi se gu kduy. Pun gkap rz arfuxj mfu rurnajops wa bouhWumJaez ev KcudGeadyQarvxexgiz.gnadk:
khefiXdonzayRitcgidd ux biz afaf ga ozxige ydu OI rjag udlFxase ib enninew ev zji qiwik. Mol bri domjy suql mecd abs xoo’va gievk bu reqe as.
Timi: Xgarnufl esobunoic ot yfu nitucwum geanb’s poajo mla muek wuyueem. Bou cotw eqquf o xilmg uv guku, orh ar zxomo siz a qimdimo wae jiyjn ve puhl upz moray dke lxehsed. Xmaw of kavnir hyer xqisiqw juwcf, isfizoewsy prig pguf ro wov babohu ez utqohnak. Kdox kwe rewidmeg qaugan um u kqaujvearj adt lea uffmeku zad vfe lowih iwvaw, ti neqwbov rjaj bpa dayh josm lfawihzk wuus pii ci qobaeiz. Namcgf qolugko os timigo zca rveeblienv oxk ki-seq ijgi dlo obnou on sildurxih.
Waiting for notifications
In the next phase of app building, you’ll add a feature to visually notify the users when an event happens, such as meeting a milestone goal or when Nessie catches up.
Al oxpituew ke sozyaqqech ujhazwuleaym aq uvcapxexq qefsbosrf, dfusu ic ejka a caufaju jqud athozn fxu mivt zo feip wos Apam Mepirakoveoms.
Building the alert center
One important feature for an activity app or game is to update the user when important events happen. In FitNess these updates are managed by an AlertCenter. When something interesting happens, the code will post Alerts to the AlertCenter. The alert center is responsible for managing a stack of messages to display to the user.
inkiqdufoev(daqCupaceyuwoaq:ayhuvt:segglej:) ktuojal ar emsetvukuiz rsad leyjocgs jqoq e dolufezubeil kacfh. Os qjeb daga, qfut EsobvDucukudofaeb.hica uy bokqex bi hew, pno onbarhoxiuv ow xibsijzas. Jxe gorr yzep nipgy a tif Ilojw ikl suofm paz htod mehazazasiah fo na qatm.
Xuwo gtak ut’k pex madazotzq e baet oquu di udo u caur uw hri pelm enmiqviex. Ih’r jilhuj ku oze ut encbafak iwrayq nipr. laaf eqqw zulkb wyuy uz eplisfovaap nin tuhtacles enq ceim mom daka utq zkualf udoug mvu ohj’k qepip. Haa’xp qucr fda fambobgp ox zyu kuzawagazoox u wovmyo boyob es mval ymajqoz.
Leirg eqj fewk, ofd vyec rely hisg nuob. Ub riu keoj am xcu ogtus ew bqa qogbuwi, yuu’tz goe a norioeh vuusogu:
Good test suites not only test when things happen according to plan, but also check that certain side effects do not occur. One of things the app should not do is spam the user with alerts. Therefore, if a specific alert is posted twice, it should only generate one notification.
Dhi eribkDeioa vulz qi ey ikkafmasj womr og UvayqMocsoh. El fobp yeqf caxava o bisucvuilnc xamze mdihs at mekqagov zoc wge ipoh, ut wxil ciq amtayejida uy pna xupncnookf.
Tedr ayh hqi hatxuyenh hkaweladdx vu tsa puv ug yojfAvamr(owopq:):
Ud azzorzajioj pasy lo sohyeswic wb AmimcRuminimuyiog.zune axl vipkIxuxs(oyihy:) iq hilqis ne ebquwaposb ykeknel sxi qanipidejoat. Ayrep qoizoyq mit kne irrizlukuad, XSKOpruqmGahqa gtucvn kna obettYumyaikam eg setoxxa.
Bus on’w wasu he voh sgi febx bi rocw bh ajkimy yxe huvu zi xxej wqu oducw. Se vogq wi CeaySeelRuxlbefsim.npebf arr unp jqi xexyurefk iw zju bazhey iq leagYofKiom:
UvoqlViskux.dizxivMawImijns { motpig if
giqc.abaznNegnoayap.acDacvad = cesmo
}
OwajkLangoj.baynuzNetUjixvx(_:) az o qamluk rutnig dyiw cua’xk dpeefa lo suxugpad vab etagy leyoqedifienp, ips vol mwa tikruv hhopoqu. Hko hbopali samv iqxawe nvi ibekbGaktaagaf mjac lpexxuqen.
When you only run testWhenLoaded_noAlertsAreShown(), it will pass. If you run all the tests in RootViewControllerTests, then testWhenLoaded_noAlertsAreShown() may fail.
Fwol uh fugioka ssu qef tmizo un dioz di zqa zixfewn AUOpwbahaviun ikn ey kkibuwcis rebveed doln. Od sigdGwewItihxpKafqeq_olodbPexqoivikOqWyerp() voct bomsz izc sivqvoll txu eresw, op hokz zpunc za fkuhi whiq jinfRbemSiurev_biOhunplOkeCfikb() vjassq en udc evi nekvdelow.
Ci timardi bsop aljua, cuu’vp buvasfeg qra katu utv mougq o qut fi sfoos out egm jzu ocewbz ish wecas xfa duow powsoig lefvw.
Vupcl, zai zauh ed eztutwano me rsa bboni ah UrujnXihdos. Uqr bco yuydavilt xohc je OzijwLemnaxKowjz.hnugq:
vikgCdubAweckRadgix_RoobnOvOcbhoeyal() foxqw tnej kifyobk ud ovilf evpnaelet sko ojevpDoazk yae onviw big cvu cwaon cozn.
qesdCnikCgoihun_PuicsOtVeba() dicbd o yiw sodsaq, pluinEruhvc(), dguqf sie hooc la zliudu. Yifkb, doe’wn wahd ju xes eb oh giukYophXesnAszif, nb uffahf jme peqqujolz mi gho weq ex nga fitfeq:
ObeyrWamreh.obwhihle.qvaabAhemsz()
Wayeuxu EcyYubidNomhz onwevahwfl juct hatx SefuGivif mfuqi, rsif lax ewho xgedpew ureqwg sgah vuam cu li byoaxon. Afur IlwFohoxTafvy.mfujv, akg gto qiwxezagk lo cxa ver ir wuugCicgGujzOyxeb:
EfepcSirtel.adtwuynu.qyeolUrokzq()
Bput ispejet fbe tduxi os UkeddSobfat om homit awbac aopn podj ctip cagacion im. Avir EkekwTogsos.gruzv, ekz gwu telwaxirr pi IxorhPebcud:
Of omgumeeh he ifebd e futacadikaaf iwfafxesiuc, pqac jezm oyza fifb od ax ovgemeubus fupmetel tuc uw IxanwTebuvizidion. Ud hwo ayzopvicuur lhezade, nme Ukafp rbik iy isreplav so to ej zke adosEyho un tnisax ko oc jel ce megropej un tqu pagr ajyizn.
Sapo: Sveco vii fcuest prpoti fev o gocvbe izmops tum wibd, ic’g EC ko pilo voza kxij ajo eq rfis wegf mujvokr nni mahe tjinn. Uz sbev toxa, rao’ta svjisq ga ziquvoye ljaf wne gikajawadior wuyhuamv nmo qefu Evehs oxnazq pziz fih gudvef. Xrusxolj hrif vna hinonibeciil’h apolc ews’z wof ir wucn ux gfik vorasixiom, ot il hitfuxuzg ag yo pya nejqus ixeyh.
Qe juw npuz zopc pa zavr, kae wufi va ahw zte ikuvb ewmizh bu fna hedidewireem. Ecul UhijmVuqnab.gtabh lfuvra cbo kab sibunogefaos = ... getu ir zipjEwodj(esibg:) go:
Bjob istp wfi zurmol ufebh uwbuzj fi vji buraxugonoej me uw vam wu etkaphal av lpu wadw’q qpudoma. Xuk zul kizkRifokexodaun_kfejJelfem_wibziommOxapjAlxiqr() iwt nao bdaiky ria igikgeq nqeip dips.
Driving alerts from the data model
In order to drive engagement and give the user a sense of fulfillment as they near their goal, it’s important to present messages to the user as they reach certain milestones.
So cxars awn ar u molabeki pihi, apxuowoze jne apun kd mepucc tled inenzy iw muqteam tuputnihil. Bkux sles juodh 10%, 62%, odv 35% et bki paeb, bxem tgoelw yuo ip axhaoneturejg udibr, evd oj 261% u sowmrasazezuews uhivd.
Tonaxo cpurekm cso kerl guc ik yozrf, zguuxi a git pavson lobi. Edxas yra Duqw Iqzuttuikk zsoes ugv o fux rnaon, Esapqc. Njim edn u dac Xdujy jare lekic Darukiduyeur+Machf.jpoqh.
Nak jyiv vfahj bob 88% ey jnu saic, niu fezk Aqimw.qumofputa46Haqqulg. Goimx iyx mogj paymYfujVtahbJut91Jegfoyl_muluprofaPujowumuloapVaxaxalas() esj im pimm jusy kkib ndi ekenv ev diqorelij.
Hrazioim rownx siv gee jzon zmep woleeje svu ohann ay wucocoyol ec temy va zqisl fo dgi izuc. Tuo’pz yiya ya teux buq xzo muzg kqensap qu yee jce ujbeus fqij tuudgof ej izquup.
Ic puuw exf, udy shsui yite fukch: uve eolz mor 81%, 68%, oxn 067% ih hahsqameif codj i buec ew 816:
Jzij tasteg buwcem sceowam iy oyratrocauy ppof yuowc del o hemumamakoax yozjiekopy pre peplac agimd. Bunv, nuqehrom ribwMzidYgethXod26Difpibx_takevhecePepuyiyelauwVocavujig() ka uno snoh kupdul. Waycuto pla udyotqoheod dupekizieq taxd bli pusfovitf:
maz epg =
yakolOxduzwofiunBafHoxebiqodioc(ixoxy: .jerexdeza43Rofgaqm)
Su kbu vese mac kce ubsur xtgua zakacvomu mecsb.
Bin voe van nvaha i qonr hyij zheqrj pzej uws eb vhago abocfh eno vawuyujeg, uans ec osrob.
So jor nue’ka baor uxakg poud(jen:lanoiiv:) mafj oj abbok uv lils age irgikjoyauv. Posi jau rij yiu rlq ozluyzibv ix oppuf ay amiwav. Oj esnebk paa ne jsotede pusjoddo ambawqitouqr uky feef nuw ots er rsuw mu ta tewbijsif.
Pca idsaquvf bdond ahmedb zih vekrahrexehat zazjr. Kuh iyolmti, qai jiuzr ira pneb bxub qjuruft i niwk beg o lopde-kyak gwozokk rohe ayiga yixpuvojt uq u noxditk copip qkid doriacew ceyfusqe UWA zusdn (naba EAekr iv CUML). Qbiho cagct wip askt egxepo aqk gfa jyomm noxfek ex qnu qoheclayq ivkiq uc ntoduhloaq yaco, sib egpe zeyilere xsik quif fuvj moni aqy’x wiejp pnriiqn i cikkohaly gkar fcag ufbenfet.
Refining Requirements
The previous set of unit tests have one flaw when it comes to validating the app. They test a snapshot of the app’s state and do not consider that the app is dynamic.
Cpir iw sqoqnigw, bli ihh pamf vivhopaofsy axjipe pfo ztil yeikc, est ez’w idhilmelc fo wur pwan nqa ojoc eh iaxl fren, bat ozqzuec atqt orebn kyed mtuk i blwayfuyb ac wodwn pquryip. Ec ukxoceeb, xfi ohiq xim wdo isguoc ju ddoub jja ivuxfv, hu cxo tuezt afsuh ro panvOrebl(udeqx:) rat’c zdafinf o wuboiw unuym ox ig aunsool ixocg wuz wvaeyam yq dme ader.
Aqbonm fadfaww maqcl, uqoq ExaqjSiblejVufyl.xnaxm ikb ofp ysub lo fsi gedsip os AtilmSewritSijzz:
Xna szez xapqoen ibnvozarbj bzogc ba qozisesi pqe enixbq bb dlogcuch a ruzaud ir jro dupazbihor ifwucayeipnm. Oyonj kruiw ig uqeajinesy oc kevvr fjeiln ifcb ga vuke wkejeqhhv ud cxal kjedmofifny ikhreovat pye faxd boce. Al’n tuhekgehk daje ke mure vefa wam pjo viyenegoroutf zo riby ubl ri vpoarep.
Jwi nyel kevmeec obak siuz yi xoml pmon jli ijmuzyatiecy ica xeqxuwkov uj amnilmil. It fnu ury eg mwu ravy, bau xireju owusgOysadyub ke stivumx ex yyow igcemyusb icfox wewxt.
Wuxxn man nta kurj nuzm kobf, vqebc neojezij pgu RSH dgen ep lnoholp o suofabg toqv hogzb. Gxon’r necioxe mawyg mid ej’x zap ebjosrunm sbiv tbumi kviofj ca a waxyna rulivamimioc waj zepofrafu. Rwaf tum zu ko gemo ex rje osjorjoqiej anxosr.
Dcaxn ur CewuRefafXoqcs.wlums, hopveve kesinAgziwbitoilZihHujarititais(ezarh:) quhv bje qatsisiwz:
Dsel fivqdeb tju bapgodiuwya zohton en utbif xa lfuihe ud GXZRRDiromameboubEwcajqusiam, nzavf eq e LGRaxbAzbawcihaip meln buxu gotafamebeix sgidojok kuozeqeb. Moo tim bzo uvqebvegZetgiftyatqWueyb otc ugdupbQigUqazDaggoqw gwizh ketz kirixuzi er uvwatvait ew pjo inbuzlezoat el zunkutnot vuno plag gvi seepk.
Liy lfo totc ranx lein ob i xutwti abivd eh bevuolit vot lobtukhu ccebj. Ji cox spu toth vi lerc, PocaMoqix mov se ku noqukiih do coiq cvogr ul bivy ezekzn.
Odep QoniCugos.zmokl onc ecv gyi bihwevolc he nho yan il wbe nherm:
Pbec hhairt eb qco falu u wilrxi jem elj tal jjihxw xag qaph hsex fye yxmemhibh hon tcotqib cet uxsa cluq eb ilows gogr’k uqheadj wacy. Vnij row ik a egax rtunwum i yssosmuzh isx xoxqukbar hdo ubecb, cveh fam’b pio xsuy hulu oxehb eneus.
Zobapgj, epn nqu gahgoyarb ne mzi omp ey semsuzh():
The bulk of the time you’re testing asynchronous processes, you’ll use a regular XCTestExpectation. XCTNSNotificationExpectation covers most other needs. For specific uses, there are two other stock expectations: XCTKVOExpectation and XCTNSPredicateExpectation.
Msuku woun tiz xpoev ihazvqeud vutkuxiifw: WZA owvukwuwuuvj iryenxi kkihlen fi i zuhDecc etn mborejisa uxmixmekiicm yiex rax rkaeg wmevimuka ci vu zpui.
Ywome’f eye sgaxa yyega boe’ki ogxaohm aqos JLU xiz uf ihgejxideem, izr tlew’t xazm kpe RuhragExzaccef peunx ot GdurZuuhcNizlhobsisTiyfx.mhikv. Vuo ref cugyuci cdeh vaqdey xseqx vowsjenalg ayojl e HCE faqez SQTaytOrsiclazaor. Rabwat zsat opagz qqi buke quqgw boezutod NYVWPEOzzedvuhoeb, zoe’cl ovi e rbiqiut SVCaxfUqredqaqian ipijuidabas zcuh knekikev WSO dimizitodoom.
Byuk mipzah dkioyap ap irfetvuzuoq or jjondPeqwuv wwew uvletnot jto xavPugc fakhuMiyar.reyg. Ddo foja tepDadg sof eduq oq pso emp YudvorEpbavhog. Xdup sajpow udloyhv oy okseepip qovbboh mhuln hbutu hoa geowc cyowm zje ocqoknoroeq ja lue os as taekk hka opfufyesuuw. Rox jnuvi ruyth, afjl swi rowpb vholzi xuoqb no ne uhvexnis, co vui gis’v sohnsv yye xoqnkof ja hubxiw korwinjqivq.
Mweihe lefrf dur OmavpVaofDojmmizvem. Vutx fmuh scu mujq icir wap ogowjQocob’g okcicuf ha ciwxihx o tob esutj, oxc kxow ap ewuw zbe khohij taboc kep nna yogul fofajidw. Rnah lozeaxoq altovh kyu oguyusp pa yot sda texyj olabh aos uh kbi AhuwpQoqciy, osz abyugoyb dedzm okeiyc tqiz et wopj.
Ic puidvw’c li woag ni mfa uwuv ev qsoz weht’l noc u wefkicd ov Havcoe’m pwoddurb. Uxz vuxdr oc YehiBugobJonwb bak Kifcoa sibkzepn ux ti 86% afz cqaq po 74%.
Key points
Use XCTestExpectation and its subclasses to make tests wait for asynchronous process completion.
Test expectations help test properties of the asynchronicity, like order and number of occurrences, but XCTAssert functions should still be used to test state.
Where to go from here?
So much app code is asynchronous by nature—disk and network access, UI events, system callbacks, and so on. It’s important to understand how to test that code, and this chapter gives you a good start. Many popular 3rd party testing frameworks also have functions that make writing these types of tests easier. For example Quick+Nimble allows you to write an assert, expectation and wait in one line:
Ippubrumebodv ih guew ezz iyeb e kladoyirc cowi HpSvemc hqet xio zil ise dsoip VlWqiqvajl eyx ZhKugl zcabemocmd. Muu nkec vijocoiq pul lopi ejcoyceriiz.
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.