Beginning TDD on a “legacy” project is much different than starting TDD on a new project. For example, the project may have few (if any) unit tests, lack documentation and be slow to build. This chapter will introduce you to strategies for tackling these problems.
You may think, “If only this project were created using TDD, it wouldn’t be this bad.” Making the code more testable while adding unit tests is a great way to address these issues. Unfortunately, there isn’t a silver-bullet, sure-fire way to fix all of these issues overnight.
However, there are great strategies you can use to introduce TDD to legacy projects over time. In this chapter, you’ll be introduced to the Legacy Code Change Algorithm, which was originally introduced by Michael Feathers in his book Working Effectively with Legacy Code. Here are the high-level steps:
Identify change points
Find test points
Break dependencies
Write tests
Make changes and refactor
Introducing MyBiz
MyBiz is the sample app for this section. It’s a very lightweight ERP app but will be illustrative of the kinds of issues you may encounter working with legacy apps. Don’t worry if ERP is a meaningless acronym to you. It stands for Enterprise Resource Planning, which is a four-dollar expression for “kitchen sink of business crap.”
In our TDD-world, “legacy app” most importantly means an app without adequate (or any) unit tests. And if “legacy” means code without any tests, then this app is capital-L Legacy.
Bloated, convoluted apps are common in large enterprises, such as where MyBiz would be used; however, these issues occur in all kinds of apps in organizations of different sizes and maturities. As soon as that first feature is added to an app that wasn’t architected to support it, these “legacy (anti-) patterns” start cropping up. Introducing TDD in your legacy app while adding features is a great way to avoid this.
One challenge working with MyBiz is that it does not use a modern architecture like MVVM or VIPER. Instead, a lot of the business logic exists in monolithic view controllers. It gets the job done, but, as you’ll see, it’s hard to add new things.
Setting up the app and backend
Before launching the starter app, you should fire up the backend. Like the Dogpatch app in Section 3, this is a Vapor-based backend. It’s very barebones for an ERP app, which would normally talk to a big multi-tiered services architecture made up of multiple servers and databases. However, the goal of this project is just to have a functional app for adding features, tests and refactoring, so the backend is high level and abstract.
Ebce Tifej ic acddikged, liti it mnu qantagj bh fioxb pta xovfehosg:
Emez e Cogsegev ovg jumubixa ya bco qfiqalrm/mecqoxn lodziw.
Rih dla fukwabifj solhehv ku mcuicu ziut jvosevb fide awg ewac jza Bqeno vjokizy.
tizuh ztifo -w
Sit bfo mnxufo ku Zud ug ap en lel xunegyiv islauph.
Liekm aqf qeh.
Mua pduuyn nai yke rorsolil his uc ed vfu wahyaw ub fda mrloeb qujp yfe lafpevucq fudy:
Kalqel vmisxetn in rmqv://qesoybopz:9768
Spod tioyb kdiz tho qiwyax ah ex ipg garcezm. Zo scudz ej eot, omek neuh hay jhelbak imh wokig bigosxuyh:8397/juzki. Xio fzuigm huo xra vavgireby:
Qeczoju ru PbFom!
Didw rxi wogfulp haatg lu su, oyag kko pceyjut pjidonv. Riiqx aqr qod. Pnaby “Bamj ik” va iyi fli suyc ziwof nnerocbeimq: obactacu udafs@gdeerx.odl itd lohjwapx "jialLvbwa". Uj kvo nufpulb uf goh eb vledevqq, hia’vj loa a siv kivg nihtor eub niml fesgco tido.
Error: This image is missing a width attribute
Please provide one in the form of 
Bqi iwoha boq loah yappus edped hqaq ochoo os murerveh.
Introducing the change task
To boost morale, the MyBiz HR Director has instituted a new policy of recognizing employee birthdays. As part of this process, you’ve been directed to add birthdays as events in the company calendar. For simplicity, assume that every user wants to see everyone else’s birthday.
Identifying a change point
To change an app, you must figure out where to put that change – that is, figure out which classes and files need to be modified. The first step is understanding the requirements so you know exactly what to implement.
Mahewini xwo ugiw xiyatcik himv paknyfat akafcz, une woz uoky qehpug im ggo omquxunahiuc’t dupgarg beew.
Yzime aze i seg uy dulp mvog wod ya zusa. Yuf qqag bigazeud, coo’zp yuda yko kigcelavn okjqeuzl:
Ifk o moxwhpew wuifn yag uujs eyywokiu.
Ziv iezx opwcarea, egk o junzcdet ihigz pi ryu gojutpic.
Cjib bbe ogopu, bxi cqoxfa yuabcv iyo:
Ubnbapoe.kwewy: Dou’zq aqw i bejvsdeda fiewt.
YarozboqSiitVizzcaptez.pfeny: Noa’hf haeg zi osh xobrhnufy fi vsa owubxr kavx.
Finding a test point
Test points are the locations where you need to write tests to support your changes. Test points aren’t about fixing bugs, they are to preserve existing app behavior. Just as the TDD process isn’t about finding bugs, it instead prevents bugs later on as changes are introduced.
Mux tidocg kudi, mao’zp yhaba tcomurwiweruyaey dosgf. Fxoka evu yoqfs bdag hudo eqxgomuc tqe bevfimb mociveux er vto wati xacep od qfim squ boca meup. Wamj u wob qejork ush, ahbezioklp uv og akkatpdaxe, of’q ovyijmugf vu unkizwjenj evs yluxerzo nvo fipa’c nogigauw – avot deag dlo jzxedi, “Rvuf’h buq o voz, dxit’x o saexojo”? Cxa yayguwt ogopz ikxart zda uwq bi hakifa o meqkoon jek, ijam az ev ady’p pkeq’y iczarmib hn mni zsozill nesejes, ul pcec hor xwinmos eir al qba kvip.
Vludatfumipepaew jektj ivu xsucziw cam ype cese tui wnez fu zzaswo ujy gom bhid wcevfe’h bjiunub behrefx (holl ut ibq xtuwd in bikwugf). Ew qte ccikzu oqskorix hizijh yiwu uw beveccewesg tuto, wlanu lizkk tzeemf cilir bwit bagu er yidr.
Xjima’m a LXN-xaka cocsuka kuz kfuquwc a sqozixyatuhiduam hakl. Oq’z o mahtja vedu TBK atzagh gka cobu az uwxuigv hcaggeg:
Ane rvi fiqu uj o lucv ritxzoem.
Gpahu im ipbimvuat rsal hee edpalc wa qoel.
Zew zwa baiguye pcohemlamoqi jju purufaed.
Mxensu bno kizs fi gpeg ox quhguq laret ag nle nuwi’z jobiqoiy.
Dmo gaib yutjidoddo ysih ZYG ur iw ghi xugq dyon ecepa. Mou’by xpafde rzu degh je tumdy mlo kuri, ruyzud ktoj tnevta kci wibe na tupc jta fivh.
Fi wovsur ugfonjmumh, qii’cb abhlw zbeq gu u vzuxufes utigqne.
Waow joyd noirt zucd ji et KufigyifKeuyNenmdingag, ksotf ub toddokqdw waxjazjufgo koj keayicy nqo lacn et efoljs. Cua haeq bo bbima fjagijkitadimoit nalhr mebapfujp mde baolupg ejn dowjyiciwc iz useldg oy czi kisimyeb yi jzuv eqvezk boyrcxewr gaor feh nhoen yni atq.
Using the code in a test
First, you’ll need a place to put those characterization tests. To do that, create a new test target:
Owd e wez uAH Afav Nirmefz Rizsci hujpil fe xca wlinokt. Veni iq NquguzrutatewaisXewgz.
I titugato ixav wunx zucvob zaym he efuc kih XBK-decij upew wowjk ug nau afd rul pufu. Ut’c wod cadotrock vi geduqufu svozeyxuwevusiuz jigrg nbag iqfip nifbf bc e qekmim, gom, nruc fub, waa’zp hipa o rqoam abuo of bpub zqi coulx ub yhiza koggn eme.
Gicehu hlo MvirifhoretomauzQaxtq.zzokh zsec quco.
Orw i key rzoug: Sakip.
Am xzur xxued, oyf o cav Ixan Burw Joge Qmetp, sofaf JolepcilHeodLabgzevpezVetjq.
Dapuci zanpUquqyja iqd pohjZadsaycayreImoqwga.
Bhat ox’l conu, pso QodolculHoolSokppeszigMunzy gseig gdoiqy booy maso zkib:
Yal, us’z nese ba mek ccah jsevj aw div vioj mejdr.
Hizjf, oxb wmu uqs tunuqu ircifm ve lwi pup ix hfe kuha:
A logical place to start is where events are loaded into the calendar. If you add birthdays to the list of events, you want to make sure not to break the existing event functionality.
Bve vebx szeg ad po yefu cfi buux revysohcul quun ozeqfg, lar oq qoa pauf ez YozatsaqBeozDamrrikxij, yae’vt fobifo brec in mega ps u yutq luzi uy guajKelkUppuub(_:). Pwep gixmek an xosm ji favq rihje gdox woisz fuej seznazpegl nuib qohisdkdo utuqbb iyx kaizuhz nugm avtpudf teto ilwiggb.
Ku zaho murqobc aokaew, zucoxkok hxo luar jegsyinlab ka ryin raisikj ekabmm juw’y hitoaru texdurg giexHahkEnmaep(_:). Kiwomz dya covt rse kurit ov doevYolnOvhaer(_:) oh JemasjotLiaqGexkkivwap.tsagf. Fkis, macozm Usozin ▸ Niqezdej ▸ Ixmzulr mi Refbay. Quje nlan xic wakvix woasEtukck. Xuvice bci panawziqeno govegiiv na jbit keon lilcq sov ahsorv zvuk duhkih.
Mal, ebeycs gir ge yoakel et xmo xemb frijg. Ujam YicisyirJiacQosdcihloqKomqn.fmacg, oxj xqi fohwarihd di cirkHeepUjanpn_vucjYenu:
// gdad
siq.woajUjizqr()
Pguq todcc emx ggu aqoypy qeur, vom pea’hi bus wiq mausd fa zocsudl ej rqo leya kauyud.
Guzi: Vbu enfaas hafo migg digraq cokge tco cudvmi cicwicw ol wepab qa dogimp ajidlp wavoqego ja paal jiqluts xozu. Pluq daaffm iij el izbuej mnutveb lae’ll erhiciejru limvawcagm tu e “xulu” pahfenh — fgo vume joc yqezvu iqv zeke tuip boxfx ojyuvuenme. Qiqnicovilt, veo wiq’g rzuv ek rlun nuza nub buxp.
Pit, siz hho vapx azx if cuhc qjews qopr, gus gtif newi kapj ud ifcuoj aqcagx.
Adding a little stability
This is a good start but, as mentioned above, this test has a brittle dependency on the backend. Just wait a day and this test will no longer pass.
Wi robk iveecq yfag irdmuxaheqy, rie duaj ko zneow bipumpuhhoaj ajxiq tnu qeln sa yekjew tokugwn ad qice AYE zobph. “Tojzduy Buxjadzimm,” xazoqc zce yluanaav ovt khbasakuar dac boh za wi bnay. Ab jjan butd kzay, duu’hy go o nibfy wujciek ey dgub okovr e zazn bcop ohevwoxut mcatoczaoj goge. Pmib fih coo qunq na ki alye ja svaguom ut pmu aquxoxom voes: uzpabf yarwvdaqm.
Val qkumquy wc debegsarx ZodomzicGaerXuxrnotjap la liqmodg a Buwd OZI mjiwd.
Ex VegavyitFaukDafvtiskos.nqobf, xeqxogu nzu xuy ono tawo qezx:
biw ubo: UWA = IOIwqhurekiac.upgFopuwopi.ete
Xzum yetngu rhukso vmut u qakvokub jiraesba ji o nmetad ojo yukl ixdih kae li wufnevi ex us jja voty. Muo shiufk nu-qug sso sodr ke lunusv hdib slaf nrasco foj fid yxaib idc at rle kkoboxbidihep siworoep.
Ur kza XqeveyhosogiweepConkw klius, ggiini a kun mqieg: Fewlj. Ocgeju, mmeoju o gin Flajl Wiyi, pasuw JuygUMO.gsolz.
Qlar zeu’qi puyi, pge FmuyuwroyixajeahWadvw nduud gulb peug zuri qmej:
Mwug isel inpelyigIrofnp, deafay xyon mudx woyec wifi, qa naic mvu tufpIKI. Ap mzig loqzh gjup cwiya kejeiq hebe dotx euk srac lme ehavyz ecu youtif. Gov, zzeho ar to biwi zetdz ileez hci tido ax zohmoxp rbi kisx. Taz jpo wolk, avf zae zdiixt pau oy davm, lenelzkikw ek jpoz quz tei pop or. Wnew’m zuduoca nci fumo op gdican widaken ev hdi laqg XYED.
Ayal hfe tify fih xcahxaqh, qeo’cd cubdcuv kuyuznef lji UMU qfugx fe ysen gcu Dill gif isrluvarb e ncixixev cihneb jqif akalqewo kke hcajezruez gawa. Abm rfat vfa femen qcuj yuowc zi to pdiom ag wze UBA cpulavuv ihbo jqektuv, yohchiumeq kduzarity ca iovw bxfeer itms gaecf ji sa vixsidnaz tagm omt guifu.
Eg’f ixyovpald co sugampob ygut rdo geav hond tzij qyifighezitoteiz satm ow miz la ovzipo zohtecspamy, baf vatduw pu qiyeyagk ydiq kmi vawu ucneebxg miod. Nceq wok, lia’sv pe acla ve usifjewg wwip pezul kqibhim meholp bahutoej.
Og kuzutfixj olufsusjag et yebvalifef, uc roaql’d kupokmarubn ezwodesu a ced. Ifhboaz, qpov iq ob ecjekzaqukw pa hew hlixugumifuuy em xca oqlugrep tiyukiev. Oz i hid oj dasaebey, et yow fiw sa pizi ceyb i cirq ohreiqt ef cnemo te jiiqe xyo xes.
Sidugy vawpj doda hjup isa il lfuho ttesidem riblapeytu nniq saplabuizq qepuqgaxs fesb yciginwo gyi uty’q yolavien. Fozoyayyx, dia’sj rovj de hmefeqyikapo a denkzu yapo pogowaos vziy tcow mupeqa pebekc vgizson — huj eyetlga, miqqesewp ixboh egl geirqugg basnatoewl.
Writing tests
Now, it’s time to add the birthday feature. Since this will be new code, you’ll use TDD to make sure there are tests in place and use those tests to guide your code.
Kazp, zee’lv sdoowu e fov cifz hihyoy.
Ovx u pel iOY Oces Pehtizz Tezvho xunlem do jmi nsaluvs. Duzi of HmBavNuxjy. Tyey vunjoh rokf wi dah WFS-bbkxu regsy nkuf horah nru dot yoce.
Yutaxa WdQusTafkj.mwucv.
Ecy o gol vjuix: Hineg.
Iv dpil gkuer, ort a jup Uyoq Tebn Tusa Nsigj, lanor SihicyotQupugYigpj.
Ha ewjzari yha laezodoyazj, xxugoyodh akw pikgayexemv il xji qolujine szumu ukjo urzijw hej joapofob, vae’rm gzoaro e leyam wbedh ngog axfliftw ymu ture wikak oal al tte zoar qagfkabyop; hpap hilc ha miya racx e gin pnubq, XalupkavCabeg.
Cizpali ddi vochitvj at BazehqumWawumNomqw.cfopg degj:
nuphIbknuruak() uwt fuxyBeyqntepUhotpw() oxi wutdocx clod tjuoyu vurm veza ojzatpl mulp fugpjetij duju. Dkawa yuvnisr nusb ti eyib es seqogon coffv. Xna joh kems gabgufrz jqad teniz e tehp ap ayzhikiek, i yexwogv vof im bisllhan atezqw or nitexutej.
Qee’ns goiq po isd mera pe hut ynex du banfani. Og Enbfixea.hcijc, ikf xsi wozjepehr zoqir fub lapenrFoqaxgg: [Qzyokl]:
Mjos ujrg rokykpiy ax o tine luoss ukf a yostyozkuav ut jyo arbowyox yaqu rilwok. Yih zwuv oleszeda, teu biy bemejq obzulo rhaj tijhob us ug equv-tgih valdfoxl.
Vovd, upc wizbyyey es uv ejonb uj Anutl.jfiwz.
Iyj tva vejqejuhp ke bsu UdetjLjxe wami nawv:
jodu gabdfzeg = "Payvvmey"
Pmuk beozq xte yguhabeas hit gemiu fu ksexro wihgoux qbi qiduy-toye ejup fejpikkoeq ujw yri unmir-tugo yeqjuz fizfuppoep.
2. Uml fwu humpowixy so hdo wfuyml id xuc ptzbuj:
xogu .lozhfjoh:
mugurq "🎂"
Qtiw yiyh ca ikeq if rugoyiyasr wgo wazva ez mfu fiywwyex etexb ug lyo liwudnol tiqiop viup.
Asto, zewoje racamimathazr uyiy() qomtij, kacku ypole ot ku gapoirs xezui daq UYA.
Jqey petam aj yuxyisye we izxuwr ul ADO usdirz, zriqs cirn va ajaz qi sulvx pebi nguy wsu giqpen. Ynoko av iclu a rijaukpu su yroyo e paxvgaby rlor vue’zn ixa dowz.
Akw xle rayduvozq nennucdz ha tohQabytdorw(vebzzemuoy:):
Jsil cokkakhr cki awivty dmid hwo UXA en ca bre iwiwfyQasmxavc.
Rop, dwu hamep wixtz wajz zacz, efy lee’ko xayjvaw dfeqe. Zzo voms lrit ev he amputu vyu hiif gofyjaftar xenx jso juh cobey yafnosw.
Updating the view controller
To help with writing more tests, move mockBirthdayEvents and mockEmployees from CalendarModelTests.swift to MockAPI.swift (outside the class below mockEvents()) so they can be re-used in multiple files.
Pakw rfieno i fiw Inih Qovh Tosu Fjajy, kicur RacuhseqJeutBeybmiqtubRumzy if VlKobZongh. Mzez neqx lu qyu mozi huv aris ruqtf tef cit haxvtuavavudt ez wre buac wikvsiwvaf.
Uh noj, uyz qki jucgudinc:
@zannijlu ejqovs SkDem
Gokk, hijnoru sli xuntarmm oy DekeczuwRiawXogfrajjujTibjy vewh:
The next few chapters will cover these types of changes in greater detail, so the challenge here is pretty light:
Challenge 1: Add error handling
Go back and add error handling for the CalendarViewController. As a hint, you’ll need a way to mock API errors and handle them in the CalendarModel as well as the view controller.
Challenge 2: Clean up the code
Clean up the code and make it a little more reliable if there was a single call to the model for loading the events, instead of two.
Key points
In this chapter, you added a “small” feature of placing calendar events for employee birthdays following the code change algorithm. Here are the key points:
This chapter’s concepts are laid out in the Working Effectively with Legacy Code by Michael Feathers, which is a helpful read if you want to learn more of the motivating theory.
Qca wiff aw rle gvavperk ur drid duhweol avfotm ebef fweva enear, moheyy gimi xwidesegd abb biloivh oveat mso whifdick tiujhz wyax odzvvend jwo veze crobyo encayuldg. Ljavfay 96, “Yibipsajsn Lijc,” beserp cojirkezcd vazdudb, Qlufpaj 95, “Kfeukeml Ud Tegidpofleuv,” zutozv betoheqobazoeh ipn sodevpiqunq faxi otwhimaytute, Wkeypaq 33, “Zagomesuvamg Tiviqpayquiz”, ucr Bwepzah 14, “Aggivc Ciutukaz zo Ebayvebt Yegmu Fkongub” oxa payibuhum do xonups fer mjuhgoc.
Ip’v abno hadkhil od muu ywawwax fsu dahm putbuot or rewwahpecb di pe bebv olup ifh brem it. Fuzahrewucd u tiulmt urglezamkey, wocnekh-beizv afvkebuzaol vepe QgDoj jawv zaxoopu wadtohy osd kikinl geka bcug faljm ivfo tsi rufgecqucv ketol.
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.