Utveckling & Arkitektur, Användbarhet & Krav

Skrivet 2019-04-10

TDD står för Test Driven Development, eller på svenska testdriven utveckling.
Detta är absolut inget nytt inom systemutveckling och många utvecklare har förmodligen både hört och testat det, men jag vill ändå skriva lite om det och slå ett slag för denna välbeprövade och ibland lite underskattade utvecklingsmetod. Det har kommit många tillfällen då jag tack vare TDD har lyckats bryta ner problem så att jag med relativa enkla medel har förvandlat kod från ett garnnystan av beroenden, till en välvårdad och mysig halsduk som jag mer än gärna virar in mig med i vintermörkret. 

Vad är TDD?
TDD är en utvecklingsprocess där basfilosofin är att skriva automatiserade tester först utifrån de krav vi har och skriva implementationen efter, så att testerna går igenom och visar att vår kod gör rätt. På detta vis ligger testerna som grund för hur vår implementation och struktur kommer se ut i det system som vi jobbar i, testerna blir lite som ett facit för vad vår kod faktiskt gör.

Redan här har vi en del fördelar.

  1. Det blir faktiskt av, vi inför automatiserade tester i vårt program.
    Att skriva tester efter att implementationen har skrivits kräver en mycket högre disciplin, och det är lätt att missa saker. Det är också lätt att glömma det, eller helt enkelt strunta i det.
  2. Tester innebär att vi dokumenterar vårt system och vi kan med större säkerhet omstrukturera vår kod utan att förstöra någon funktionalitet.
  3. Designen av vårt system byggs upp utifrån ett användarperspektiv och inte från ett implementationsperspektiv. Eftersom vi skriver testerna utifrån kraven på systemet.
  4. Testerna ger oss ett facit som hjälper oss skriva rätt kod och eftersom att vi skriver testerna innan vi skriver implementationen så visar testerna vilket problem vi behöver lösa härnäst.

Som utvecklare är vi väldigt lösningsfokuserade och vi vill gärna hoppa rakt in och lösa problemet direkt, men med TDD tvingas vi ut ur vår lilla bubbla, ta ett steg tillbaka och reflektera lite extra innan produktionskoden skrivs.

Hur ser TDD processen ut?
Den process vi följer i TDD är ”Red, Green, Refactor” och den är relativt simpel.

 

TDD_RGR

Red
Vi skriver ett test som beskriver hur vi vill att en viss funktion i vårt system ska fungera. När vi kör detta test kommer det att bli rött och inte gå igenom, eftersom att vi inte har gjort någon implementation av logiken ännu.

Green
Vi skriver den enklaste möjliga implementationen för att säkerställa att testet ska gå igenom, och sen kör vi testet igen. Denna gång går testet igenom och det blir grönt.

Refactor
I sista steget är det dags att ”refaktorisera”. Simplifiera vår kod, ta bort duplicerad kod om det är möjligt eller strukturerar om relevanta delar i systemet helt och hållet. Helt enkelt göra vår kod bättre och mer förvaltningsbar.
Vi kör sedan alla tester för att se till att vi inte har förstört något på vägen. Och när allt är grönt är vi redo att skriva nästa test och börja om processen från början.

Varför ska jag använda TDD?
Det är ett lite ovant sätt för många systemutvecklare att jobba på i början, men efter att ha jobbat med processen ett tag får vi en stor trygghet i det vi skapar och mycket är tack vare att vi har testerna att luta oss emot. TDD har både sina för- och nackdelar såklart. Några av den vanligaste har jag listat nedan.

Är det inte slöseri med tid att skriva alla dessa tester?
Många anser att det nästan bara är slöseri med tid att lägga ner energi och resurser på att skriva automatiserade tester, detta är nog det vanligaste kritiken man får höra. Kunden vill ofta ha resultat snabbt. Men ofta ligger även en stor del av kunskapen om koden hos de individuella utvecklarna som har varit med i projektet en tid, men med tester så får man en dokumentation av vad koden, vilket gör det lättare att sprida kunskapen och därmed lättare att få in nya utvecklare i projektet. 
 
Ja, det tar lite längre tid att utveckla simpla implementationer med TDD, men den tiden tjänar man snabbt in i takt med att kodbasen och systemet växer. I början kommer produktiviteten vara lägre än om man skulle avstå tester, men det kommer snart en kritisk punkt då ett system utan automatiserade tester helt enkelt blir för komplicerat och invecklat för att även den modigaste av programmerare ska våga göra någon ändring.
Med tester som har varit med från början blir man mer säker på att de förändringar man gör inte förstör existerande funktionalitet.
Därmed utökar man värdet för kunden, genom att minimera införandet av nya buggar i systemet och sedan lägga ner extra tid på att rätta buggarna i efterhand. Plus att det är ganska skönt att som utvecklare bara klicka på en knapp och köra tester istället för att manuellt starta upp systemet och testa funktionalitet som läggs till om och om igen när man utvecklar.

Blir det mer kod att underhålla?
När vi skriver våra tester kommer vi med största sannolikhet behöva ”mocka” bort vissa delar av systemet och denna kod kommer bara att användas av testerna. Den här testkoden behöver underhållas precis som vilken annan kod som helst i vårt system. Det är lätt att glömma det eller strunta i det eftersom att det ”bara” är testkod. Men tar vi som vana att städa vår testkod efter hand som vi refaktoriserar övrig kod blir det inte någon flaskhals i framtiden.
Med olika mockramverk som NSubstitute eller Moq får vi väldigt mycket gratis och vi behöver tänka mindre på att mocka och mer på att skriva faktisk implementationskod.
Det kommer bli mer kod att underhålla när vi skriver tester, men med olika ramverk kan vi minska det drastiskt.

Vad ska ni ta med er?
Det jag har tagit upp är ett litet smakprov av vad TDD kan bidra med i ett projekt och hur man gör för att använda det. TDD fungerar till exempel utmärkt att kombinera med BDD, som min kollega Dan har skrivit om i ett tidigare blogginlägg, "Hur BDD kan hjälpa dig lyckas med ditt IT-Projekt" klicka här för att läsa det!

Men om det är en sak jag tycker att man ska ta med sig så är det att faktiskt skriva automatiserade tester för sitt system. Det får vi gratis med TDD och i min mening är det lättare att skriva testerna först för att sedan skriva implementationen och inte tvärt om.