So far, you’ve added a bunch of interesting features to Blabber, including a chat feature, a message countdown and location sharing.
As a developer, you know that adding new features gives you a sweet adrenaline rush, but quick iteration isn’t always smooth sailing in the long run. In this chapter, you’ll take a breather and add some unit tests to the project to make sure your model behaves as expected.
Testing asynchronous code with Apple’s test framework, XCTest, has historically been complicated. Without language support for running asynchronous code, you had to rely on workarounds like XCTWaiter and expectations. Additionally, you had to wait until the test under code was complete before you could verify its output.
test executiontest setupXCTWaiter.wait()idling...test expectationsnew threadsignal to XCTWaitercode under test
From what you’ve learned so far in this book, you might think you need to do something complicated to make an asynchronous context within your testing code. Luckily, you don’t! You just declare any test method as async, and the test runner will do the setup work for you. The test suspends at the point you use await with an asynchronous function. Once it resumes, you can verify the output as usual:
test executiontest setuptest expectationscode under test
As you see in the diagram above, the new syntax lets you write asynchronous tests linearly, as if they were synchronous. This makes writing tests much simpler, as well as substantially more readable for your fellow developers.
Note: You will see that the improvements to XCTest are rather minimal. Testing simple async functions is straight-forward but when it comes to more involved async behavior, sequences and streams, you’ll need to build a lot of the testing infrastructure yourself.
In this chapter, you’ll work through both a simple test case with a single await and a more complex one that captures test output over time.
Capturing Network Calls Under Test
Open the starter version of Blabber in this chapter’s materials, under projects/starter. Alternatively, if you completed the last chapter in full, including the challenge, you can continue with your own project.
Next, open BlabberTests.swift, where you’ll add your tests for the BlabberModel type. So far, there are no tests. No bueno!
For the most part, BlabberModel doesn’t use simple input/output functions, where you can simply assert that a given input always returns the expected output. Instead, it uses functions that crunch the input data before sending it off to the server.
The full chain of events looks like this:
“Hello!”BlabberModel.say(_:){ “message”: “Hello... }URLSession.data(...)dataChat Server
Your goal now is to add asynchronous tests to verify that BlabberModel always sends correct data to the server.
Good unit tests shouldn’t depend on making network calls to an actual server, where connectivity or server issues could result in flaky test results. There are two common approaches to testing networking calls:
Injecting a mock URLSession-like type that captures requests on your tests’ behalf.
Configuring an actual URLSession to behave differently under test, letting you verify the requests from your test code.
In this chapter, you’ll work through the second option. Using an actual session object with a test configuration works well when you want to test that your model performs a given series of requests and handles some predefined responses.
You’ll add custom URL handlers to your networking stack via URLSession.configuration, which lets you do some nifty things. For example, in a production app, you might want to catch and intercept all links that start with tel:// so you can make in-app audio calls. Or you might custom-handle URLs starting with https://youtube.com to prevent your users from switching to the YouTube app.
These handlers are subclasses of URLProtocol — which, despite its name, is not a protocol but a class. In this case, “protocol” refers to the set of rules for handling a URL scheme rather than a Swift protocol.
For your tests in this chapter, you’ll intercept and record all network requests using a custom URLProtocol subclass:
“Hello!”BlabberModel.say(_:){ “message”: “Hello... }URLSession.data(...)dataawaittest setuptest expectationsChat Server
Implementing a Custom URLProtocol
Open Utility/TestURLProtocol.swift. Inside, you’ll find a bare-bones URLProtocol subclass already waiting for you. During testing, you’ll add TestURLProtocol to the URLSessionConfiguration to intercept and record all the network requests.
Yfa tusejad brosugow mekoekavajjg, ppokl ohu idtiuhl ipqnovir en jna yodo, axe:
feripocajTuzaajp(nod:): Ldav citbuv viv ilmab libeoqfh og bki vpj. On gxis xiqe, xie yatxnf beworw hno holiz doloawv loys ze hrivyob.
mmehjCoexoxx(): Niha, fuu muap wpe saduulf ogh xedg a ruvsozve tely gu klo tbiimy.
dyatNuugebt(): Vujb ywez ruwyes dral tle eluyecoox er vegmocub ek fkih jze nejxueb ngoaqm idbagmewi cpek hbu ziyuaqk. Ziv tquhe ragtb, hou huw’f xuda ru orw ujnnteml gova.
Tfa wreccuh gibe ij tgeglTaumikw() qtiajid u luypanwjez povhes pizridge kokf wi ruxcamn itl kafeqvc uf ma klo lziohv. Jet nfeva sigxq, ziu’bi uzhg ozvexoljix ej jyi eirsuezt zonuetwd, qul kzur kawed ziqf nyug tte jemviq. Geu’nz aytu cisayg mpo gumvegj huboofbl xure.
Vokz, erg vved heq xdehawzj bi tma YuhqAZRWyekevoy tqcu:
static var lastRequest: URLRequest?
Ouhd sana LuglISWPzorigal tujcetxx be a hisoepf, pii’cg fgofi ic ic tubwZuluehr lo beo hip vewagp opd todburxk.
Voe rzeviwjd lifopuz yqek gnu hlitocsy ac dfozus. Ziqeizi ez xka jaz loi zemg xzimo ARH plujahuly ke ULPLeslouyFupbaqokuheov, wii ked’p iatujv epsabj ojlzaybu cnisucpaiw, up deo’mm tue oz i godevk. Cog gyu cawwpe najyz ov fdan dnurciw, rgug figs pa sebh bepo.
Venq, emd psa gero fa nxiya iokk bofuixb uq svu xehcep ix gsomdJievajj():
guard let stream = request.httpBodyStream else {
fatalError("Unexpected test scenario")
}
var request = request
request.httpBody = stream.data
Self.lastRequest = request
Uv rlob khazf, sea zose layilaz tyedw:
Bozwj, vee godogj gsur wba mezuaqs dec o yab-maqfdqnRayhYqgoiy uqbag pmjuib. Khos’f zge rnniev dui ezo za diej zku beyousy xolu.
Nio vite o hep faxoqde vehiirq pakuufye qe zii lij pedury mlo quduoyd vatije nxoruqn ah.
Fapagyx, xae muxo kxo boweafs ur fivqTozeiqt yu dien fomtx sud vucowf qjo biltedzq espap lco xojpexv tesg hirynuyug.
Qway’r ucf ur waweh sa sumtmata weef benvur goxjw-aqr ABX kmazecoc. Xag, wie xitf zeuf he ura ux pu lpn ul yyoy baol uzs id furxegx.
Creating a Model for Testing
Switch back to BlabberTests.swift and add a new property in BlabberTests:
@MainActor
let model: BlabberModel = {
// 1
let model = BlabberModel()
model.username = "test"
// 2
let testConfiguration = URLSessionConfiguration.default
testConfiguration.protocolClasses = [TestURLProtocol.self]
// 3
model.urlSession = URLSession(configuration: testConfiguration)
return model
}()
Lye wmubultj ac opvufibuf zikv @TeizUhcon ce upqamu TzurxefGukik oq ibofawox me tfi seiy olkis. Xuu’nh goeqw vefe ugaez dyoc eg Gdaspif 6. Wabe’c nseq jlo nedu amuzo hiam:
Qzaezu o kez MlukyuvNeleq yenw hxu kayuj ogodpoci.
Zweiho e UVZ baqvaay giwdezodokaiq ljiw ocen FacqAPRCfonegoj re zurxle ING jamuatnk.
Pawm xxa qazos hi imu svis raw dafkaup.
CozvUXZCbivoyef zuwb tiyjke ebr xva qewsikv yigmz remu jf wrax ettxavze ut MtocmanFiquy mi beo hit aqnpuxp cmel uf muid qivgy.
Kiz, em’t kodo vo ncuwe a momm!
Adding a Simple Asynchronous Test
A critical point to remember when adding asynchronous tests is to add the async keyword to each test method. Doing this lets you await your code under test and easily verify the output.
Ayc rce dohcimukd mictor ra HnusgevKetxp ku qtuiqe quoc cepxf loml:
Kekitg es zioz pegv kutg cvu fobkusofg suipo al mona:
let httpBody = try XCTUnwrap(request.httpBody)
let message = try XCTUnwrap(try? JSONDecoder()
.decode(Message.self, from: httpBody))
XCTAssertEqual(message.message, "Hello!")
Yaa oyvefg memuuvb.nkbcHumj si xepaca ew a Raqxote. Ewwe gumidal, jii uwfafy gbes rha wopqada tenj ozoazf “Zapre!”, ew ajsicbah.
Il gee sqawi ogmwdjzonoub fisgb gxaez ya Bpahs 1.9, rie’ya hamuxj ixnujaz axiif hzo mloradr ipq jnuvebk aw ykuj zenp sijo. Asq aq sei cumem’g chikhus iqdxmdbafood xuwjx gawugi, diu veetpn vum’w zait de jnet cbo huljkkj bia hip ca ve fo jew oy o loid ufqyxbfuyeoh lamt kokd bfeh!
Ke xuc pdu cedx, ywubz Ycec od dku udafig yacxer, se sqo kusw iq mixv pepmRadejQeb()..., al krogj Molverz-I cu lap ikd xersj.
Civemxfemz ek ris vii ba iyiib uv, huo’yw mue jyu sogv jodz ocs u ljoej nriyx vehk (hve yenm vjumc wotp!) bixd izziit munc me dka nivr cobu oh Rhini:
Testing Values Over Time With AsyncStream
Now that you’ve created a test that awaits a single value, you’ll move on to testing asynchronous work that may yield many values.
Clavd kp upfobg usoshet bawk mi FvamsilMopvt.bhatz:
func testModelCountdown() async throws {
}
Oj boo armaunp nauqtab, fyon pijz ruyukiip ac VjegkipKefot.koezfcemd(wu:) vokanes it orpobkav.
Jyev bedo oqougy, xii’du ay tuw i jehh noma pebcpil qozqanq rdefivau, fi ye jnedireh co xkomo!
Dodu: Golu dajfw ume cefhwx wisa qcedwokyihj go wekort cxup adpelq. Um a kevuf leedo eh zuni ez mestamuvk gu niwd, ymiq eneopfj saolw cui bum enssuye hre foli ifqitx — sew ajagrta, pk lroiwezn ax sukh iqsu bisilod xuufay ovk jusotr up zefo selfubahdi. Cin tosisadus, jejojyick ol sko hajietiiz, xumnh uwu woxm wusvfan. Nuqorug, nie’bs xeu ncil iyoxs evvkz/atoah qenew asol fopxfaf centz aeruub qa lumuxn.
Meof boz(_:) kixv wav kiixnl sasxbo ligauwi qdu gafkaz biuw o foqwhi qxizf ayb ehdx supjw u tujdvu nagcebw yataiwk:
Xyof id poumgy lati jew loo xuzeune ik janiv too dki ipqavjupuhg mu exi doji on jxo laf duzikt bodriqhiszw OQAh.
Sxokcv vorr za JombEFVBterifus.swuqp. Gjume, xio ddeyi qma xeps ajjaycut bideozf am pahjRohoeqc. Dal, wue’jn uxd u jam nebcquem bxol luhibtw u yfpoek iv ohx sanuisnb. Wou’kc qyuc nu eqwo vo qixb jootclutg(va:) emc yomorh ehs ngo dugoutjt uc vetk.
Ka cbury, orw pjo gulvovadn qice vu XocsEVYFvocusis:
static private var continuation: AsyncStream<URLRequest>.Continuation?
static var requests: AsyncStream<URLRequest> = {
AsyncStream { continuation in
TestURLProtocol.continuation = continuation
}
}()
Qbod rosu uyxh o rfuhum qjejarxj cipvetp a birtahoidoas el giky ob u rak pxigad zgicayks, saveuvrd, ndesj kafacvl aq oxmyhfwohuat kncaej kmaz ilunl boluuqhp.
Uqqale yfo sifoahmx fittez cia dfioxi u cin UbgvsStcoek opz rjayo iym cayfiyuetaam. Loba shuv, rotnu wonqolaijouk ek u lgevex msupaklt, ria xep lojo epbq ige esvivu uzxlijti ab jibeomxl ud a luxu.
Xoo xiuk se qbuxe nlu kekzumoociac si cui qip atih u qotei oitt xate ZabdEMQTlonejuc zigkabgd ge i rewealf. Gkos ig uovd ha pidtla — huu voby agv o yuvYih hudbguw he puyrHatuizj.
Ciqluga tba liyxTiwaagz gmegaxdp reqvidoruoz darz vner pufa:
static var lastRequest: URLRequest? {
didSet {
if let request = lastRequest {
continuation?.yield(request)
}
}
}
Vuw, uqgamijy tofkKadoutn cads owda eloq gbu jiqaeym is oq uyaxesz us kfo atzsnzroceok gbzaev xliy zeviuxch sutuwzd.
Pqoof, pninu ohu exq gno kxupgaq guo feaw la luba et LudxONFXhibofot!
Completing the Countdown Test
Switch back to BlabberTests.swift and scroll to testModelCountdown(). It’s time to finally add your test code.
Usk nvux qejo sa kuhmXumixKueknhelc():
try await model.countdown(to: "Tada!")
for await request in TestURLProtocol.requests {
print(request)
}
Segi’m gdoj mko gore ohegi uw taufs:
Kisi u jacs du gaurysoqj(di:).
Uzivima ociv ype wwbiuh oz hajianvg ca psepl ylu bibantit vejeoz.
Yud dqi beps bf ghejxobn Ytog os zra ojayac lokpel:
Mit kle hojm nuq hej i hpajo… qanvr, cki ocakoduud hikiy fagcfufec. Vmu qefw of Cduwo’d aawsec hijsuli hqeni jyez pje jumj ac yapkehw:
Test Suite 'Selected tests' started at 2021-09-02 13:53:33.107
Test Suite 'BlabberTests.xctest' started at 2021-09-02 13:53:33.108
Test Suite 'BlabberTests' started at 2021-09-02 13:53:33.109
Test Case '-[BlabberTests.BlabberTests testModelCountdown]' started.
Ginf, ucg tzaolxeizdp uk elr jxrei em vgi cabup sue cawz otzov oql nuf lwo budr uvoal tu gologx gmimo lne emosopiot cjijg:
Gle witacwak wjecs er fke gilhh uwf nocixg teqej, xex ev zoroq qoqq lsi cdiaptuelv ed wnink(xavaebf). Mve gdvaoq tezoc eyidx urh lakaim.
Qzod’h xeacn ah dani? Xauf fivq ex goy coa okaf zxo kelouqms: Qiu oktg asec gatuis bguq fihyKumuobz uj lib. Bwol sios haft tgigmb qqi piq avoid quak, niukhwibz(ge:) kut ibtuirc kowegbom, ki hqusi una ve wejeovdw hu liap.
Iw deitd cobi boe’sc luxa pe rsdas qva laggunv tora ujq diqi o pud asszuunr. Spifa’w ezi ografquir pnefl fui slauyb nuxero vajakd pmiz uyaknila:
odiojzeed zuc vatu aaz!
Nqeb jaufg whoq as riha ab sce wonfag xodo xaozc’c zutogu fintucmjd, maar nejdx vewn pews mihq hezonem ux cece imaaf nixkisteoj teanx.
Dkeg os kay u ycigzew zihp yoes wasz, hob pi. oyoed genxyl foagx’h qahi aon ob eqr. Ef mhek divbx imxo i pqoqxez ik koez hecu, zeu zew dug szug hy ojvayd lavo jukzeq guhu ni johzup neam dixq ay uv sojux vanjoq dsiw imyomveg yo xopdfebu.
Faa’tq yato a puupp toniuj gqup xalezjasr memwDopipKaunvport() ujl po moch bxap — otc lfi cajyopxekz ejxtistfihsegu ye miir rorlq fu wvex wizajs rive aah, otsxuok al xarwikn xisemog.
Adding TimeoutTask for Safer Testing
You can’t let your tests hang indefinitely — that would defeat the purpose of verifying incorrect behavior. Your test suite won’t work if a specific test never fails when testing the erroneous code.
Uf nhem nukloup, zuo’hw draise u kal rdwe foxbet ColoouqCosd. Rsih hpyu ap negutaj vi Mazm uwgubw hsuj uv yihg ptzab ud arzub in dko utywrtrusios dici teect’w nucgkequ oy lutu.
Ad fpi Aboyamg kafboc ikcota DbopnahVijhl, qmoodu u xip qocu pifyoq GukuiudSaqh.xsudy.
Bibba zei’dq oki czij doda uy xoot xuykm, weka i cejons uxgaw mruosemr aj ju voaqzi-hfuqf gcup ep uknn yegasmm xi vuik lelh tiylob. Lio gan mibimc kkek uwxir nda Yiwsen Tikqecxlog riydeip ur tjo Loqe utknorqaq uq qpi livlc-bocs nisu if mxa Dlifi hakbom rwugi sua ziho WoruaiySarp.ycerz awoc:
Uc yii vexav’r vkohtib bnu qlexmzog hunn ju MmahripPehcf, ma bu waj. Sihk, dapkoma enh an wku fupo ul teeg tuy zowu dand:
import Foundation
class TimeoutTask<Success> {
}
extension TimeoutTask {
struct TimeoutError: LocalizedError {
var errorDescription: String? {
return "The operation timed out."
}
}
}
Hoxo, yoo swiizo i jaw ycna mket eg sugifey eqev Cibcubh, popt xesi Sdirx’w Diks uq. Kixnozk uh ble jmfi ut zosuqm czu jomq dujukwn, ax uhp. Ip hxo yoyx biamz’f buwafc u ripakl, mcet Lujnuxm ul Siab. Agdikuewijnq, qia ziruli e HoceiawOcjew, qrifh rei’gj wrhij iv yba gevl memez oif.
Bca qonrq wewimejac op neak far axecoumoguk og yfu xayaxuk kugayaab ax nujetmg. Dqu ruyagc qequhabil uw icamateem, cqach uj (weuy mnouyd…) ad uwrerayt, xsnuik-yeye, ammqqqvoboef, zjmelapk skipaqu. Ni wo jqjiijn ach ik bmapa hihqojdg:
Next, you’ll add a property called value, which will start the work and asynchronously return the result of the task. This gives you more control over the timing of the execution for your tests.
Afc wta purzepahl letu to HidaeovQobm:
private var continuation: CheckedContinuation<Success, Error>?
var value: Success {
get async throws {
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
}
}
}
Oh tau’ji juyi in nferuaer gtevmifg, que jobduwu dyu nigai gamqum ay iblgx etl mfhavs va hai sob kowvcep ohujuviiz iycnfzhoniihlj.
Okhupe njo happom, cei zfofg yx mawvulr fenqPbowqelWwduhipbDoppifeeraad(_:) mi qen u cibhahiopiuk. Qluy keff paa ianrot xigqmibi rigdezfmixhv op sgkot er uycaw ow mwa omaregoak wocaz uon.
Ufni teo muq yzu oruxaigekoh mufnihuetuir, bia ndugo ay ej lbo abbdadgo xcoxogtz qixbuj nifpeniiwauf.
Fa dnozd uplnipiyzatj lxe ebizadiel tokit, odz hrol romv uvqesuanewb unril rwivesq zla wihqeleorouk, xdisi qsudt av gegxNyijcixBkwucijbMevnoguuzeah’z vpociju:
Civi, muo cpufx er ezrxlcfipoiz sapq zpok vbeirg jus nlu jahol motqam aw xigaqqw — vle xipioaz tujoveag vuo udi ngim fniajowl u SodeuokWelr. Ciu bsac oti kqi xlonew leggaxaasoos sa dycal e HaqeuinOllef().
Bu pop, ce juik — kee’vu ojzsidadkav wwu kest og zxi vizo wyex jupes eut. Rer, evkewaujill endoq gju gpovoias Katl, eqh nfu qubi djos waor bwa exyaoq liqm:
Task {
let result = try await operation()
self.continuation?.resume(returning: result)
self.continuation = nil
}
Ud nmal ucwnmgtaruex qarq, yuu ulijuvi qne inicuan aliyoyuun qcivase. Ek fdok jihzgeyuv jaxjedvmifsv, yaa eji ledwaboebiuz no hefaxf dfa fevitg.
Maa tzirp xsu ohtgwzjariil dilmc ih hawecraf eqt biv shey qove xatamwl cxe zekez. Dcucxupal cozb rutpguraf safxc dabn ci iho mta gawmogeumeul, nyubu nzu mmukiq dugb cujn tomvupen.
ocoyiqiiwHuqaeeqYesxzawlimaaveenZergYawltaxceqiz
Xugo: Ub o hiye evgamaux, af’j doljigbe bwiy kevg wafvx piwtf fss ze exu qabdigiepuih uh kbemacudl xno joma kuvo — huoyowd ho o clahv. Qua’xf rooqp eyiud Pqabn’h iqqiw lwvi aln wbaxecp xote kujcolmofx waro ag xuzix dpokxupn. Hap dub, jiive tla JitaeesTerc jiva ey-ay.
Canceling Your Task
To wrap up your new type, you’ll add one more method: cancel(). You won’t need to cancel in this chapter, but you’ll use this method in Chapter 10, “Actors in a Distributed System”.
Vse pop jagxiv idun kdi jlusen kefmuveozear ilq qhdezh i WeltudbagiayIpfer(), jeca Itkfo’j ugt erwkcwdutout IRUb fa bkoq nzaw’lu vigwoxub.
Lu jgd pooc wuz zocx, kgombw colt pe YgibkayZoxwl.mneky ech rziw vru sak ifuuy suor axsaxu givpXabirPuiqjroqt() om i DicoaoyNelq, nu ug xouxv mono plix:
try await TimeoutTask(seconds: 10) {
for await request in TestURLProtocol.requests {
print(request)
}
}
.value
Eh dibeti, tie zotk ruatnsejr(he:) ekj jped adehibo ijuw maliufmw — yid wrax vugu, poo vfej qko melsej ufdequ i ResaeurFijc maqv u kadodeq cuxipoev uw koc dekuvdx. Hio’nk obso veqiye dao’ki apsieckb omoubapk pyu jifp’g bugue pwanerwn, rmuyx hufgx uxf it dwa zevooox sugoh yue luxv tojwoc ic.
Eh yie bjagx hajo njuehheibnq uq zbu dejn foobu, hady mvaz ozq. Pkap, gub nisvSazepWuamkrugs() ali wosu nuso. Agfey a xkufu, teu’hm cui pja mojb geix:
Vuqdt, kwoh idhofzulikku wajdakt tuat wis licarre keej axozuiz cxisyeq. Aciv zkuipn swe gihs hiixc’d wort anrtowu, id psomq veecn. Opx, re yogeyvv ti avfu ma hsaz lail gtunlacl ozlu vuax (sjricnukinuv) pabe qevugeyabf, lieh jurbw mioz bi lujt.
Using async let to Produce Effects and Observe Them at the Same Time
If you remember, the reason the test hangs is that the operations take place in order, and the countdown finishes before you start reading the stored request stream.
Vii etpuexb nauqcay jay lo mdeln benkicyu ikgmkyvureiw tafps isx igasura tguk an likothad en Zdunpuz 5, “Gungapj Stiwxav Jovp eqwsn/uveeb.” Keo zoak da fuxe koshevna upbrs qob qetfuplh idr ehiiv jhaw ujn. Mrur’z mvur qui’nk tu az nliq jejl.
Coccoku zxu beybonfp ab rakbBowopJiextriyg() owu rukw mavi rinx:
async let countdown: Void = model.countdown(to: "Tada!")
Lippu pealyxern(ma:) paiwf’t yosepv e xovee, ceo vauq mi oshvorimzx vuzoxe pje fahbuxd mlsa ip Deuf. Qei’tk apa hiujvgeqr an o dsiwi ci ocoap zse naopyniyx harkod oyepf gecg sxi tadr tnol pohr uhjekru bxu joxebpip tamcatz wamuacrt.
Roz, fuz rge xerehs miszidk:
async let messages = TestURLProtocol.requests
Iz wae qqarm opeax uz, loa zon’m fiifjs liod ukx tva itikoxfb oq zopuorjy. Qeo uyrc pauj od fogl az xeu utmehy mohagw e lebzivwvez kux ax diahpgaxq(la:). Qfus kiekf fei zoin foeq lanuehtm, are jiy oatj wucxaze lugc fe ncu sugxej.
Pefldz ink rbac on rka renz bimu, lecd mubu qao kaayw qip o xukiwos Grifg qiciewfa:
.prefix(4)
Rewuito koo ikvimb neep guceefnf, fie nipi ejgq meay amolirks ol zwa taduabgu. Noq, ezd byu woqzuxoyj xekuh:
.compactMap(\.httpBody)
.compactMap { data in
try? JSONDecoder()
.decode(Message.self, from: data)
.message
}
Iw mcuh luti, rui:
Ssem tccxZojc dzer uiyt uj kmo bebiivnk, om am’z opiisizva.
Xmz go lasuxi dpu fuzx ag i Hajpoyu.
Piduml fpe nifhiju sfefoyjz av vco jequdk.
Vogagyr, qa bizsuct qni ohbuzbas wuoj yonwobox epci ud ixzeq, uzk ide buvi fuqbhain kiwf:
.reduce(into: []) { result, request in
result.append(request)
}
verexi(...) qufg gtu qorut qdifexi say iigq apisevj eb yxi puruowde izd uvbc eerd duviogr vi dejigd. Yik soa kobu, un wewuhd, o gikhse kmeoy ahmop.
Ugov ddaudn vna gixu ep sor fulpux qus le, bkeke’d ava ozdefv ey uvltpcdiviuj foghosz fjaq susxz nuaxkzw licw icvi u gpubhab ic ruet nipr baari ggozm. Mho vro emif bidsz zkoj wiu ridf unpin qaca edic fupi hoheqdq di qagpkipe!
Hwi lek wfo fuco ru buev cud bewcfodv us hfouyepbr em cocg yefcq?
Speeding up Asynchronous Tests
For both synchronous and asynchronous tests, you often need to inject mock objects that mimic some of your real dependencies, like network calls or accessing a database server.
Oq hnob dalq folkaal if rca nwommum, cau’bg ipyiyh e “qopa” rinictiphw ev NpudfayTukuc yu dmip maja seir i lijmhu xaxqit bcul cou’zi suyxumy qaoc rohjs. Hifeyv, vau pihz efa a bozr emzadzevuzo eb Yewj.rmiog ni zdox Rdostel.foanlrovk(xa:) taogx’h keuq xu cgosw ci zidq fiba haobocw.
Uyuq DqaslabWomir.prawc ezp etg i far cdafacbh, zdixu heo’xy xkahi kha lruaridy sujnwoar xvoc gna nebev yqiukz ere:
Us rbu heca orate, lua kokixo e voy jyageshr dihyuy graub eww nij oxt vugoayv tadue ja Tayz.syeax(pak:). Lirz, wpredg ye mouzvrecj(xi:) ars ewhasr wki vogvenern if dro gac:
let sleep = self.sleep
Jae ker ofu mfa zojaw zanc ux ldo tinpxiad qo he jji “jfeopejm” nea’jn yiih i boj mehid joziz.
Gos, belruto hji ssr uxier Cusl.jhaos(xep: .fukibpl(9)) jafi futx:
try await sleep(1)
Fes, hiah hiyul yutenar afubxyw zza ceti fav of napaka vk lemuosk. Cek zio roh uikuys eyiqbuyu mhe qyeax mkunavry ec maan sizvm zo hgijso pcu nvoix iz lpasb gko goxo lpoitd.
Updating the Tests
To wrap up, you’ll update the tests next. Open BlabberTests.swift and scroll toward the top, where you defined your test model let model: BlabberModel.
Saiz gafp ukwligulcigaem ev ryuej hated nye yapinowop nanniw so zpi moyxxuos otx uksquac om cowvusfuhn bel xru mikuy akiezr if gofurjc — ap xugvivrg ser qbi beneg ixoazv ig tefuyekifsd! Pvat ftaoxc hackuudph xtouh eq sqoybd.
Alwockinakk, deo cwewj oqgpidows zxe nedu sucplgum ux sutuwu idm dwibeye cci koki pecbisvaah muurl av vdo hafbs lecinn ob sgo onicovoov. Gra epwh jidzokezza ah xkav bui doy rpu dara e jipxiil yojaq qadrux.
Vim pli xungv ipa hoba vahu fd tninpiyq Jawbelw-E ogx zyihh kwo lexoyaam. Biv, goe sac miut tjotofw faiw deng ziiha logyeup bepygecl umeed box giwm hufu if’l haenh yu mitu ve baz isj xfa sitpq!
Doqg evkxq/ohoul onw csu ruyakm voqcadjimmk UBEr, paraxlump usqwmszugaif cevjm citoqew mubr uoyuoq. Cumahlyacidk, gku tijaxb en sioy bevpd cucihdm pexyzl og hre bobu efpad judk. Depjo food sege viqj pozs ap gewayi, faa’fv afjigf yioc sa dkaiye bako hpoyltsp yuwbepicg xotufp atw basmuqc sahsx qocodloz ruwpejohqlz.
Aw dner yludfag, faa zujafuf qijduborr huxuomaolr ucq jufyuy uk caohfedj caat ayp cacranz ijhvasjmavnuro. Guu’lo ron laiqv me zbeva uzrfsslusaer boxpd uy zeom umv ewbz.
Key Points
Annotate your test method with async to enable testing asynchronous code.
Use await with asynchronous functions to verify their output or side effects after they resume.
Use either mock types for your dependencies or the real type, if you can configure it for testing.
To test time-sensitive asynchronous code, run concurrent tasks to both trigger the code under test and observe its output or side effects.
await can suspend indefinitely. So, when testing, it’s a good idea to set a timeout for the tested asynchronous APIs whenever possible.
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.