För ett par dagar sedan satt jag och läste lite i boken Working Effectively With Legacy Code och då hittade någonting som jag tyckte om. Inte för att boken har varit dålig hittills – det mesta jag läst såhär långt har varit vettigt och matnyttigt. Men just det här tycker jag förtjänar att lobbas för. Det jag tänkte skriva om här kallas för characterization tests, och för att kunna förklara det fina med sådana behövs lite bakgrund.
Boken jag läser handlar om hur man ska förhålla sig till och arbeta med legacy code (orkar inte leta efter en vettig svensk översättning så engelska får duga). Även för en inbiten programmerare är definitionen av begreppet i boken lite intressant och kanske aningen kontroversiellt. Översatt från engelska anser författaren att legacy code helt enkelt är kod som saknar tester, oavsett om de är ett ”arv” eller ej. Med andra ord: inte bra.
Att man som ansvarsfull systemutvecklare ska skriva enhetstester för den kod man producerar är ingen nyhet. Det står i stort sett i varenda bok som behandlar ämnet programmering. Om detta är så pass vedertaget borde väl ändå de som arbetar med programmering/systemutveckling gladeligen ägna sig åt testknackande dagarna i ända? Well, som vanligt går inte teori och praktik hand i hand. I vissa fall (läs: många) finns det inga tester överhuvud taget, i andra har intentionen funnits men man har inte nått ända fram. Vad kan man då göra för att försöka förändra situationen till det bättre?
Enligt definitionen av legacy code behöver otestad kod kompletteras med tester för att inte längre klassas som legacy code. Det låter enkelt, men om testerna ska verifiera programmets beteende gäller det också att vi vet hur programmet ska fungera. Här stämmer bokens budskap och min erfarenhet väl överens: det finns sällan något enkelt sätt att ta reda på vad korrekt beteende är. Och vad är korrekt? Det beteende som finns att läsa i en gammal spec begraven i versionshanteringssystemet? Eller systemet som det fungerar idag?
När det gäller characterization tests är inte korrekt beteende det primära, utan här utgår man helt enkelt från systemets nuvarande funktionalitet. Till skillnad från ”vanliga” enhetstester, som syftar till att verifiera programmets beteende, syftar characterization test till att beskriva programmets beteende. Returnerar funktionen X i dagsläget värdet Y? Då ska testet förvänta sig värdet Y när X anropas. Är detta korrekt beteende? Vet inte, men det spelar ingen roll just nu. Det är det nuvarande beteendet vi vill åt. När vi har satt en struktur av tester som ”ramar in” funktionaliteten har vi skapat ett trevligt skyddsnät för framtida ändringar. Vi kan då börja fundera på vad som är korrekt. Samtidigt kan testerna ses som en dokumentation över hur systemet fungerar. Att sätta sig in i vad kod gör upptar en stor del av en programmerares dag och här kan sådana tester vara till god hjälp.
Har jag provat på detta? På sätt och vis har jag det; utan att jag vetat om det har jag skrivit tester utefter befintligt beteende för att ha något att utgå ifrån. Men det fina med denna lärdom är att jag nu har ett begrepp att förhålla mig till. Och när jag kommer tillbaka från föräldraledigheten blir det till att börja praktisera det nya begreppet på allvar.
Så nästa gång du blir ombedd att förändra funktionalitet i ett system och berörd kod saknar tester vet du vad du ska göra! 😉 Happy coding!