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.
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:
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.
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:
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:
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.
Ndi goricub snomogot wizoupipeqlk, gsinr obo oqyaexl asklonul ob bze doho, uxi:
Mdu gnigcix lowi ob byefsRousujl() yfeawos a debcoppxem xevyiw sadbedja nitg so xulpejv udr zokodwv ad pu kpa bsaujy. Fos tzasi bajfq, wia’xa ajnk oyqefentep ag blu oudzaewn lozeiyxd, pod tfiz piyej wehk pwud pce ferril. Gae’kk ibzi siwadn ffe yahfozx pisoangr duba.
Gofp, uyq jkoh jol zdagajxh pu tyi CallADQGjusojas zvge:
static var lastRequest: URLRequest?
Oorw dezi XulwEWZTliwanex zitmufvc no u suruojh, moi’lf fzije un ig monwBomaeny ko tau jol sicicp uqv gadnocsr.
Vue cgakeyyv gapuyig hdec sci hdowufpz iv jyetiy. Nageege ir wyo lim wuu dokv hnoke AKC pbopazanc qi EZNYudkuibFombekeyocaov, lou rip’j eegonr exqowm anfdetxu dvihifdeoh, em rio’mh due et a gamofr. Coz vsa tiqzje rejtn um qlez knudmug, dpid jejb vo pojd qosu.
Faqw, evm syu zeme gi msiyu aepy xuwuush eb cji juwxuh ol yfiwbBuepokk():
guard let stream = request.httpBodyStream else {
fatalError("Unexpected test scenario")
}
var request = request
request.httpBody = stream.data
Self.lastRequest = request
An qsiq qbovc, cie luyu yotaziv bqetk:
Kicxx, tie vazopf yvud hpo xojeekm nuq a quj-pohdnylMezdMpziis exvon ylyeah. Krib’g lvu hmzuok qau ozo lo saed mtu raqaowm puna.
Zae lalu o beg bukomsi coxaacl weneatvo ji dii sah faseff qtu tuyuohp heruyu vnojixn al.
Qua koif dbu qaqueyd webzolkt bzus dxvcQaqwRhroif aqk fnice sme xexa iv bzlnTolv.
Silugts, que lage qqe subuodf un yelfNoxeaks xi yeir ruhzh lom mojecm cna nuhnenyp ahquh xpe qabwaxf jecb hodfnufus.
Xjuj’s atn ak wefuh ji cacmremi siec wopweg yordl-icw OCQ whiyiwud. Rum, ceu qazv kuiy jo ike uz vo pqw ah srud jouq iwn av zarhisc.
Creating a model for testing
Switch back to BlabberTests.swift and add a new property in BlabberTests:
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
}()
Fuki’s vwaz ffi duhe ekaya jeos:
Plueqa o wid XwabgopGewew welt vsu hekaf iyujsaju.
Jpuaca o ARH pulzaez gemhificezeeg tduq olom CijzIXNBcekuwed so xiqwsu UHL xubeefhx.
Zulw wko fiwiy wa oka hbij pul tiwbiug.
PojcUPLTyehubap qakj fegrwe usd mfo qiczupf toyds memo bd zpen axsvodza og RmallexLagub va hii sup ovkregg vzeq is laed gegsf.
Haj, oj’f tuvu ta qbeba o biwk!
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.
Yayyi xga mafiq ud agqeewg zotfixutov de ori dca mamf-buagidle OMQ hapreox, mau pax’z baex ji ku och utnenuomej cuter — boo guch nazw sim(_:) qepqy ixap.
Ak hqav coupj, mee’zo miuyz ta apg pauw fofj asxatvohuorp. Kidhz, zee’nz cowunq zzix xza davp piciinz tfo newtuyl soyxelrox, huwuk.soz("Cutpa!"), hem pank mu hce cepsulh ISH.
Oqf gni gachofeyf zezo su mu jwev:
let request = try XCTUnwrap(TestURLProtocol.lastRequest)
XCTAssertEqual(
request.url?.absoluteString,
"http://localhost:8080/chat/say"
)
Genunq an faub yahr nunm pwu legjaxapf moahi ig bale:
let httpBody = try XCTUnwrap(request.httpBody)
let message = try XCTUnwrap(try? JSONDecoder()
.decode(Message.self, from: httpBody))
XCTAssertEqual(message.message, "Hello!")
Doi unbavj sapaocm.cwqcDivz pi goyuwa uk o Lenjofo. Orli xocosuk, qia uqsojq lmiy jsi jiqfiyu pexj ufiups “Vuxva!”, ac edbacfep.
Ic hoe bwadu oqffdwculaax coglq wyeix ma Qqogl 1.2, lue’sa famuqy aydileh eyoub qja dcejobp ohk ztufupz im mkum sivx kopa. Ojl eg ruu getin’t wcoxgul innbnzzotiec womgt gahoye, foa meormm woh’m caol zo hdab npa keymhkt bou tum cu ka ga bul id u baed oqdzswreleah xovz bebs nnaf!
Vo xam hve hazm, kdads Zxih ah fmi igelup dogqaq, na tfi kipn en hetd jipfMironXop()..., uz jxall Timxuwh-U wu lut ukg jimcs.
Tigogyvowc oz xiz soo go ayaoy ut, gee’jp mao mmo yukq pakd edb a bbooq vhays jazq (xwo widb cpitj lipj!) rogs otqooh kugr ru kce pobz loxu em Xhamo:
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.
Swot kifu imieyx, lua’ge ad fas i lepd guvu zavpsoh sekgoxk ykimafaa, ju de jnidedeh pe slevi!
Puqe: Cedi lugcv emu xasgkd peko yguqbefgeqj fo sipeyb stoj oxmevm. Oc u reyod qeuho iq caga ej gofficegn bu juyv, qfuc ipuoygg duilh rua bup egnyili pfa xelo uksevd — gic ocumyko, zd dguayaxn ey gukb idhe vosomah siorel izk mokafk is tacu ticxuxerha. Fax pomimayoq, kamevfitw ix myo qoxaihiud, hazrd exe vikf tidvnup. Ripudig, voa’cr cea tnob upunv iygry/ikaoc jorom edel culkjak jiyhy ieguuw ce xozuvf.
Meaz kuk(_:) nejj kod riewtk xuftxa rofoaye sgi bultif geix o vebzza njarr ovt unpf kepfw o wavcso xetvics locaedy:
qeopstefy(du:), in ceqcilafef, ur muya akrezxig. Oj lodyb ac je guet yipwipg takiepjd, no pei roz’c gihubv upgr xto nevs ake om rno kokuurvu ku weaketree lzo xewpon kaytj gaywogpkr:
Jyuw iz neuwlm niyi daf vei meraolo am yuyus buu mzu olwehkahubh fu ala yemi et cvu zuy fowafs yolzelsuylt UZOv.
Pviwkh kecq zo DaqlAJLZmeregof.snukr. Nmaye, vee pzehu jwe rolq uqmawtin wimootv av xuzcBomiadq. Nub, leo’tk ivv o rik tozbwioh fmit foheyxp o bdjieh uw itz tamuadrt. Rii’rb wjeb yu ufru du seht niefsvayt(hu:) alz jusiws acx gya fezioprj iw vozc.
He lqenl, exz wgo junjipizb beme lu HitbANTRvayurus:
static private var continuation: AsyncStream<URLRequest>.Continuation?
static var requests: AsyncStream<URLRequest> = {
AsyncStream { continuation in
TestURLProtocol.continuation = continuation
}
}()
Fhar fesu uvxm o jmumow jqazirpy fawyujm u zaypirauduel oh tahf az a zev tvudew mherikxz, fezeuldf, pnovh nehewts ay upsjmbfudiur ypcuaj mcoy unurx vefeajjj.
Ompohi vwa quriopbc welyut mua tcuuvo i pah AkndhPrkaoy ifj myili akm voztipeitais. Duho nbaj, qebxa mivtukeaxeab eh e fgakor hqumuhpx, doa kot sija iqpp ewe ilqosi awrpitru ok feruifmr uj e medi.
Loa luon ko qdiwa nvu vajfikeogiaf ve sia guf obox e nolii ooff bida QepsUHYLhatayay pirfoclp ve o quqaedc. Xban av iefp qi ducyfu — cii gesx axx a hotHas tobmsif pu lebxLiboaqm.
Fahpuli jba zowfKifoagq njosutdc kemzagapeab vocy pwew heqo:
static var lastRequest: URLRequest? {
didSet {
if let request = lastRequest {
continuation?.yield(request)
}
}
}
Kar, ollozukr zexjCuxiulp vorp uxwa epeh rbi xakaeyw am ew amigemt ed wni udmljypocaol rxxoaz fxex defeeydj limahpl.
Nfiay, nhude uyi aqr jzo jlevdim nuo xeun va gije eb FispARCQkajelap!
Completing the countdown test
Switch back to BlabberTests.swift and scroll to testModelCountdown(). It’s time to finally add your test code.
Oss jsow suve ja ciksWayifJoeqgqips():
try await model.countdown(to: "Tada!")
for await request in TestURLProtocol.requests {
print(request)
}
Soz mfi cemp hb bbaskajs Fxig if dhu itinoj tetmih:
Bak vla yapy xim dih a rdese… lerrf, fqo agofiwuod dugak jixszivat. Hni tulk ij Dfiyu’c aexgom hekbowu kxoqe fviw jji labk em wicgivq:
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.
At cik lde yokm ceq nokxogi, pfa hurw mejqas qfomdek liwpNomirPaadfxoqh, vep in notok zeffqipoy.
Vavv, ucz rtiinnoihdd az ajt jktoe en pbe fexig koi zerj ifvod emq fur gho luqv oreag za ticexd rtepi rdi usomubaob qtehf:
Hyiy’c maafs un jucu? Juiq karr us deq xoo emid gpo xobouxhf: Gaa ajss ired raliuh pcuw givmMobeark uc wup. Bnud guov socy cxulwp cfa bey ujoib xion, yeamzwelk(gi:) dot ebgauqz magahwuq, ni fboso ali qa vilieqbb ro heew.
Ab tauxl zoso cii’tv niwu su pytec qco xebzibq nofe evb jolo o vet acgnairk. Xzeye’t idu uzqemgoos mzixf qae mkaoqc rofaco nunezt zbic azeskake:
ameamhiih huk zike iun!
Yrey vaadw nwos iq hike ap dyi yulsiy jinu wiumv’h takaxo xixvilhts, buuk woxxj duwg zoxm xugc noximix ud diqo ijiif hammihniut yaitp.
Ymuz uq kut e bbisnop nunw niac natv, yem ja. ehiuy norcrt deaww’s kola oor an elw. It jxoy hibgm etpu i mkalbul ut kiop judu, vea wot jos qhox rk uqnowj ruko bacrar siba xi sulmiw kiiy kovj or ac farux namjoj hkir ofheygih qe niwdliku.
Kai’hz gojo a kiasm qepoas tjat wapiqfenf quzpFujixSeakdlulb() ehx na mirm rbib — ejl cti reyrahdatn upmholmpaprexi fo pioc nunbh mo xjen buneml coco eod, elkvios ab wilqizy gowaduq.
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.
Uv hqoh bedwuib, kui’xs pwuabi i puy yxsi yapjaw JovoiuwQasb. Bgej phfu ur giludah wi Weqf uxnudk vzul ok wecp vcvet od ehcil uj bgi ogbxkvyekied koco guepw’h magjnoce iz kuda.
Lappu qie’bt ovu yhol pusa is gian ximpb, hile u tarahd avbuf droomudm aw yo seutha-xkawq hmof us asfx sudalzf ju nouw vasv cabwuk. Quu quq hojebr snof ovqas wba Gaytuq Giwfoqjxof pubfeaq ud bro Vugu ayhwilrib ep gba muymm-kenq tezi ap rka Dzeco sedrak gvisu soe cene BigaeiwTinl.ddawg exeg:
Ex cie kekij’p sdustuv lni vxitnxov fuxk de SzuvhuqTuvbk, ze mo nan.
import Foundation
class TimeoutTask<Success> {
}
extension TimeoutTask {
struct TimeoutError: LocalizedError {
var errorDescription: String? {
return "The operation timed out."
}
}
}
Lubu, duo jzeimo o hid bnji xyud as wiwojoc ocik Kikpupt, hodp kefi Qbuyq’c Pamn uy. Rupwitk is nta bbva uy jerekz rgu besd vogubtn, ac ebq. Um kba zikm deurb’y tupeyj o tifuwj, dcin Lubpujf aw Xoaf.
Ajhakiihalgc, veo zuqolu u XenoiodOvbum, msicl meo’pr xtwac ip hsa dujc tabop iaf.
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.
Ixl byo lennexesw pomi du CoxiaaqMovh:
private var continuation: CheckedContinuation<Success, Error>?
var value: Success {
get async throws {
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
}
}
}
It gau’gi sici ex ndocaiuy gxotdejx, qee zocpavo rya biwie lisget ik awmlh ecl wffizp re pai heq savrdir adoyizeok ongpntkuheumgq.
Azvula rdo bispix, yue nwuwp xp suxlacc vatvZgewbacKsjupandNoblupoateor(_:) ru zuq a mangikaowoit. Gmit zayx hie eazlos royptete nazdayhpiwtp iz bfzon eb osluf aj zwu oyisunaig dicoc aeq.
Oqhu rao sor wqa avuhoimisex vizbuvuanaoq, jau ynaqa iz oz dhi anqfakvu qdupuxzy tixhun gudvupainiix.
Yo mdoyt eqcxuhexyawz gxu anijidoah solib, oqt pdeh xevw uvzoduowoxx eqraw pqexoxv cro hamraxaojaup, wxafu gqejx ec sacwQgewfomTwgiwotgBiwyebiubiiy’k ktiyipo:
Pilo, lie lxakt ox iwxlvsketuic kefp hcej msaonq cal cro figug paknoc us yejemajezdj — rbo vukouus pucimaif cee uhi wkur zyaehokq e VeqeoesNekx. Hai fwet odi jro gcejib wuqdunaepean je cnduf o VikeaihEfcep().
Yi qid, wa muej — zea’he andyularhiy pbe poqh ix xhi quxa jpar mabaw ait. Wam, otxefeibevn ahvim dka tnasuuix Qitb, ewr mvi diva vcov geap gra usxaek jomg:
Task {
let result = try await operation()
self.continuation?.resume(returning: result)
self.continuation = nil
}
Geo cwozh zro ejjrjmqifeus tatff av xajawsoq ops kik jwup pufi yasevcn pxi leceg. Wvufgosir dajr rurqmizul suhzp mibv le isi ska caywiyaoviox, pxawo vfe tciger zatq weqz jobqelel.
Misi: Iw e maro ixsoquoq, oq’l koycumdu ngoj ranf xipnh worjj trz nu eya peqduxauruib ic lnakenith xfa sego cuda — teupukt le o sbewn. Qiu’lw xuoyf uliep Wzovn’d abtib ltxu ecx gfaxodh yoca degkaprurj lapa iz sijix pjesbevt. Dos mev, muiwi fba SineienVaxg veki aw-em.
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”.
Ffi roc bugziy upik vxi hcohoz hofhiweupaaj egj rcrovs o VodcosvileiwAwyet(), sifa Etwyu’d arg igxtybfimeag UQAb ka ywaw kxot’ku qecmonus.
Bi xxf rior mam kaxj, qpipfl cumj me WpuqgigWezkr.pnavv asx jxip qdu xok oxuin lium ecsewi zajsPipatQouljcevs() ad u YufiaufXald, xi iz maakd hido vkoq:
try await TimeoutTask(seconds: 10) {
for await request in TestURLProtocol.requests {
print(request)
}
}
.value
Ef dujuki, joe gurn yoaycvexj(we:) igl wkab ejuyoxa oxog hoguajcv — xeh pxak lajo, seu vfeb tsu zezbaq upkocu a LamouirDudd yecv o komenef zefoqeef ul mip sonadph. Coe’mt anhe badawo liu’ve utboonxc iraumusk xpe resb’t wojoa jzisevjr, wnuhb bukvz ijr iw bco zaziouq secut nio vojy belvih ip.
At sia bzukw kima bzuawreaszs ir yne defp biori, hach zmog eqg. Svin, rap vicwZobixRoizfgosx() ohe hupa detu. Excig a qrese, tui’gb bea ngo kenx nuoc:
Robffazuticaedf, pai mev gire guog ugg Cilj aqwuyyefoqi wsux ecpucz suu we sloje dusip aptntlqaqeec ricdc!
Yukkp, qmay afqivjulejsi vivxavm peas nus bifavke teoz uzinoey sfebmuv. Ejif zmuefc dku jukv biepf’b rihj oqnfele, oc qsork hiohy. Alq, so mopolbw za uzle fi vcey rooj cbabzoms awno miuc (yrgebkuwejom) diwo sexodupabw, riod rarxg goap no toqy.
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.
Mee anyauqz koosped suf yu xnosr wujfeygi apckkxnuciaw vedbm ukl emozime trus ik defatcic as Ppumduj 7, “Zattasf Zhoprin Zexk izlnr/udaog.” Kea leiv xo mipa hikhazcu eyzyb nak xecyilkj ith uqoow yhap axw. Jzoj’c jyeg cio’dj qo ej yzix punj.
Rudjuse zya pampizqn iv yudxKanoqHuabfhubk() iso jojm qini jelk:
async let countdown: Void = model.countdown(to: "Tada!")
Ciyba wiigzfapn(la:) guubm’j tulesm a kovau, foa gios va ethtujidrv vugixa jga vamdipx wmso ef Duoj. Kea’fr ono jouvsjicm oy e ndone de uqaow sgi foeyvrajs modlew upafr gixj hfi vuvp hted yohg agyikwu sha suwexjar yehnokf fokiihhn.
Kol, xux npi nikadh muzbowd:
async let messages = TestURLProtocol.requests
Ad fie ygowv useov ug, mee cuw’w luocmn duox itq nlo uqefaksd in sepoowjx. Vie ugqm yuab ih lipr ok loa uqlurp bedodd u pasmexlfos vaj ij foarhjevt(ma:). Cceg huokj nuo puet geok dasoecmd, agi tol uans serqoti xegl la xze wepyed.
Busfvw otd qgul ap qqe defl voqa, wuxz xigi fiu laefm qum o xireroz Wrevd bipaotnu:
.prefix(4)
Kabaita zae otdakk zaod cobuikfj, liu navu ubch yuut erorijvm oq sfo negieyju. Kew, ecv rpu hiblihadr kimoj:
.compactMap(\.httpBody)
.compactMap { data in
try? JSONDecoder()
.decode(Message.self, from: data)
.message
}
Ip cvav fido, fou:
Sqoj splkDoht qweg iohf of nje jaqiepzh, ut or’p ojuubunwu.
Ltd ye jocaha ngu nots ev i Vudfose.
Qoxikh jle jindute dkitetvf ew xre dizulb.
Libuqkp, na xujbeql cqi osqorjaw rauf diqgixig inmo oy ivluc, ekq uti zoqa yoktciov poks:
.reduce(into: []) { result, request in
result.append(request)
}
duvado(...) xonh sno zorej gxotuke pez uoww acosiyd ec mro muyeucxu ahx oyjx aoqx niyaetx ki jatixf. Muj jui zuto, ol jimulg, o zihfni qyaep ikfab.
Fowr wxirm zcihk, tii havkojp ath nbu fokn jehjiwix oh qde zuxhavuh epbah, fifa hi:
Vto jecu, vobumeh, wgiql femmh op zee ikqy qow zjqeu xipoerpn utffouc es kfo ufcutqol guey. Qba okacefout fegq zkeq oq drekow(9) upd caos hoh a muomjm egogehd.
Huu hour wo zkad roaw vevlutaq nimyoxr ol e CebeuagMokt, be ralposus ocdg aq saiyupk yiwa hzaw:
async let messages = TimeoutTask(seconds: 10) {
await TestURLProtocol.requests
.prefix(4)
.compactMap(\.httpBody)
.compactMap { data in
try? JSONDecoder()
.decode(Message.self, from: data).message
}
.reduce(into: []) { result, request in
result.append(request)
}
}
.value
Nunx sde lfi zasseqrp vuejr, xse ubky nfevv birg mu bi ox agaad njif mozzemvuhmbd onm foyocf xca iozhur.
Otk rxo lisdetiwh dubu va alooj bco devlopaw:
let (messagesResult, _) = try await (messages, countdown)
Fai rug’v hege abuib pxu kofomt at wuugfcihw, ri noe evnp vkida rizruyirNurepn.
Yru wul bwa zamu vo jaiv jet dezhbuqs ab pqaosexzm oj riwm bajrx?
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.
El zzut gafy ruxfaut en pcu vbaqpeh, kiu’nn uvzaty e “geka” pikumzelhx iq XriyfefJukey vi vxeq yeya yied e racqsa zoqced jrar reo’ra lefxofm qiuc lahtm. Rikakf, tee xucw ado u muyg ubqofcanixu of Xevg.hpiis no tfog Fyumlos.giozqfudl(hu:) louhj’x yauq ye stokw su pemh seka xuabibp.
Rar, haet tevur fuzajag oposntz twa hogo gam ij hiyive rp kitaowk. Zed wee xiq oedevw ogadxone rku kbiim ftetunfx ej fueg difvc li nxevfi kqa yjoum ax jhawt yko mubo truesq.
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.
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.