Wat zijn de voor- en nadelen van Test-driven development?

Iedereen die een app laat ontwikkelen, wil dat de app een succes wordt. Het eerste wat je als gebruiker opvalt is het design en de gebruiksvriendelijkheid van de app. Alles moet er piekfijn uitzien en intuïtief werken, anders haken de gebruikers snel weer af.

Je kent het misschien wel. Je bent net aan het scrollen op Instagram en plotseling sluit de app zichzelf af en weet je niet meer waar je was beleven. Het crashen van de app of andere vervelende bugs zijn killers voor de gebruikerservaring. Al deze vervelende scenario's kunnen vermeden worden, door hier in de ontwikkeling al rekening mee te houden. Een test-driven development zorgt voor een beter resultaat op de lange termijn. Hebben we je interesse getrokken? Lees dan hieronder verder.

We gaan in deze blog dieper in op wat test-driven development is, hoe het wordt toegepast in praktijk en wat de grootste voordelen van test-driven development zijn.

Deze blogpost is geschreven door onze iOS developer Mark Meesters.

Wat is test-driven development?

Test-driven development (TDD) is een development-methodiek, die afwijkt van de 'normale' ontwikkeling. Bij deze methodiek werk je namelijk precies andersom. Normaliter ontwikkel je eerst alle functionaliteiten (lees schermen, interacties, acties, etc.) en schrijf je daarna testen om te valideren dat alle code die jij hebt geschreven naar behoren werkt. Mochten er testen mislukken, dan ga je achteraf de code aanpassen.

Bij test-driven development pak je dit anders aan.

1. Je start met het analyseren van alle requirements die er zijn voor de app.

2. Vervolgens stel je acceptatiecriteria op, waaraan voldaan moet worden om de app succesvol op te leveren.

3. De volgende stap is het schrijven van testen op basis van de gestelde acceptatiecriteria.

Ik hoor je al denken, maar hoe schrijf je testen als je nog geen regel code hebt geschreven? Dat is dus het unieke aan test-driven development, je schrijft eerst de testcase en zodra je die hebt gemaakt, ga je ervoor zorgen dat de test ook daadwerkelijk uitgevoerd kan worden.

Praktijkvoorbeeld test-driven development

Stel je voor dat een developer een app wil ontwikkelen voor het bijhouden van huishoudtaken. Één van de vooraf opgestelde acceptatiecriteria is dat het mogelijk moet zijn om nieuwe taken aan te maken. In dit geval wordt er een testscenario geschreven waarin we een huishoudtaak kunnen aanmaken. In deze test wordt een huishoudtaak aangemaakt en daarna opgeslagen. Als we na het draaien van de test een nieuwe huishoudtaak zien, is de test succesvol. In het begin zal de test steeds blijven falen omdat er geen code is geschreven, maar bij deze methodiek blijf je dus code schrijven tot je testscenario die is opgesteld aan de hand van je acceptatiecriteria succesvol uitgevoerd is.

We blijven de code verbeteren, zonder dat we het gedrag van de code aanpassen, want het gedrag is opgesteld aan de hand van de acceptatiecriteria.

Wat zijn de voordelen van test-driven development?

- Bugs worden op tijd gevonden

Laten we maar beginnen met de belangrijkste, namelijk dat bugs vroeg worden gevonden en vrijwel nooit mee kunnen komen in een release van de app. Omdat we beginnen met het schrijven van de testen en daarna pas de code, wordt alles altijd voor een release getest.

- Releases zijn betrouwbaar en veilig

Dit haakt eigenlijk in op dat bugs op tijd worden gevonden. Namelijk als we de applicatie test-driven ontwikkelen, betekent dit dat releases die naar de klant gaan betrouwbaar en veilig zijn. Alle code is namelijk eerst getest en elke test is geslaagd voordat er een nieuwe release van de app gemaakt wordt. Dit geeft niet alleen de ontwikkelaar een fijn en veilig gevoel, maar ook juist de klant, die weet dat er in de code eigenlijk geen fouten meer kunnen zitten en de app precies werkt zoals het hoort.

- Kostenbesparend op manueel testen

Als je gebruikt maakt van test-driven development betekent dat zoals al eerder benoemd er voor alle stukken geschreven code ook testen zijn. Dit betekent dat je heel veel functionaliteiten niet meer handmatig hoeft te testen of te laten testen, dit bespaart je dus elke release tijd en geld. Er zijn natuurlijk altijd rand scenario's die je wel zelf wilt blijven testen zoals de interactie (UX) van de app, maar daar kan dan ook de volledige focus op liggen, in plaats van dat de focus elke keer ligt op de basisfunctionaliteiten zoals inloggen of het aanmaken van een huishoudtaak.

- Een bug komt nooit twee keer voor

Een bug? Die kwamen toch nooit voor bij test-driven development? Helaas, ook met TDD kan het voorkomen dat er een bug wordt geconstateerd in je app. Hoe goed je ook alles probeert te testen, de gebruikers kan soms toch zo onvoorspelbaar zijn dat er toch een bug doorheen glipt. Maar mocht die bug dan toch voorkomen, dan zorgt de TDD aanpak ervoor dat deze bug hierna nooit meer voorkomt. Voor elke bug wordt er een nieuwe test geschreven. En nu is het eigenlijk een inkoppertje, omdat voor elke nieuwe release alles wordt getest, betekent dat automatisch dat deze bug ook getest wordt en er dus nooit een release kan komen waarin deze test weer faalt.

Releases zijn betrouwbaar en veilig

- Clean code

Als ontwikkelaar streef je altijd naar schone code (oftewel clean code). Doordat je eerst testen schrijft en dan pas code, betekent dat automatisch dat elk stukje code specifiek geschreven is voor die test. Zo zorg je er dus voor dat elk stuk code een single-responsibility heeft. Hierdoor is je code heel makkelijk te begrijpen en blijft de ontwikkeling overzichtelijk. Dit is niet alleen fijn voor de ontwikkelaars, maar ook voor de klant. Zo kunnen fouten in de code snel geïsoleerd en opgelost worden.

Wat zijn de nadelen van test-driven development?

Huh, wat zeg je nu, zijn er nadelen? Ik zie je al langzaam afdwalen, maar zoals elke methode is het verstandig om de nadelen te noemen. Het grootste nadeel voor iemand die een app wil laten ontwikkelen is dat de doorlooptijd van het project langer is, de kosten van ontwikkeling hoger liggen en de allereerste release hoogstwaarschijnlijk later is dan oorspronkelijk gepland. Dit komt doordat je bij deze methodiek niet meteen begint met het ontwikkelen van de app, je begint eerst met testen te schrijven en daarna pas je code. Hierdoor zul je in het begin van het project minder snel resultaten zien, wat mogelijk voor spanningen kan zorgen.

Maar is een snel resultaat altijd een beter resultaat? Eigenlijk wijst praktijk altijd uit dat dit niet zo is. Als er veel bugs na de release gevonden worden, kan dit het project serieus vertragen.

  • Zo zal de ontwikkeling volgens deze methodiek meer tijd = geld kosten, maar zul je in het einde talloze uren besparen aan het eindeloos blijven testen van de basis functionaliteiten.
  • Zo zullen alle bugs die over het hoofd gezien zijn, niet voorkomen en zul je je gebruikers dus niet teleurstellen met onnodige crashes of bugs.
  • Zo zullen alle stakeholders die gespannen zijn voor een app release, nu zitten te popelen om de nieuwe app met nieuwe functies aan de klanten te releasen.
  • En zo zouden we nog wel even kunnen doorgaan, maar ik denk dat het wel duidelijk is nu ;-)

Test-driven development in de praktijk

Test Drivin Development in de praktijk

Zodra we over TDD praten, wordt vaak de metafoor of vergelijking van een stoplicht aangehaald. De metafoor van het stoplicht past namelijk perfect bij deze methodiek.

Zoals iedereen weet heeft een stoplicht drie kleuren: rood, oranje en groen. Elke kleur heeft een duidelijke betekenis in het verkeer:

  • Groen: Gas geven! Hier mag je lekker doorrijden, jij hebt voorrang.
  • Oranje: Ga ik remmen of geef ik wat gas bij? Dit is de vraag die iedereen zich stelt. Maar volgens de regels moet je stoppen als je dit nog kan.
  • Rood: Remmen! Rood betekent dat je moet stoppen en absoluut niet mag doorrijden.

Hoewel deze memory refresher voor niemand echt nodig zal zijn, helpt hij wel heel goed bij het uitleggen van TDD. Bij Test-Driven Development doen we namelijk in de basis precies hetzelfde. Alleen betekent elk kleurtje net iets anders, en kent TDD geen oranje, maar een heel andere cruciale derde stap.

Fase 1: Rood (De falende test)

Dit is de kleur die je als eerste zult zien als je binnen TDD een test gaat schrijven. Dit komt doordat je binnen deze methodiek altijd begint met het schrijven van de test, en dan pas de daadwerkelijke code gaat schrijven.

Je maakt als het ware een blauwdruk van wat je gaat testen en daarna ga je dit pas invullen. Net zoals een architect eerst een tekening maakt en de aannemer dan pas gaat bouwen. En het wordt nog mooier: er mag pas gebouwd worden als de blauwdruk is goedgekeurd (de acceptatiecriteria).

Omdat je in het begin nog geen onderliggende code hebt geschreven, weet de compiler niet wat hij ziet. Hij zal direct foutmeldingen teruggeven. Bij een foutmelding is de test niet succesvol: hij faalt. Rood betekent dus: je mag hier nog niet verder.

Fase 2: Groen (De test laten slagen)

Zodra je de blauwdruk van de test hebt geschreven, ga je ervoor zorgen dat rood verandert in groen. Dit doe je door met zo min mogelijk code te garanderen dat de test wél succesvol draait.

Het scenario

Eerst maken we onze test aan. Deze test is heel simpel: we hebben een lijst van nummers en deze nummers willen we van hoog naar laag (descending) gaan sorteren. Sounds easy, right?

Swift

class TDDBlogTests: XCTestCase {    
   func testSimpleSortingDesc() {        
       let numbers = [5, 6, 3, 2, 1]        
       XCTAssertEqual([6, 5, 3, 2, 1], Sorter.sortDesc(numbers))    
   }
}

De minimale oplossing

Wat we volgens de TDD-methodiek nu gaan doen, is er alléén voor zorgen dat deze specifieke test groen wordt. We kijken op dit moment nog niet naar de bredere acceptatiecriteria. Hiervoor moeten we eerst een Sorter aanmaken met een functie die sortDesc heet en een lijst van nummers accepteert.

In het codevoorbeeld hieronder zie je dat we de lijst direct in de hardcoded 'goede' volgorde teruggeven:

Swift

enum Sorter {    
   static func sortDesc(_ numbers: [Int]) -> [Int] {        
       return [6, 5, 3, 2, 1]    
   }
}

Tada! Nu zal de test groen worden. De ingevoerde lijst wordt exact teruggegeven zoals we hem in de test verwachten.

Dit werkt nu natuurlijk alleen voor dit specifieke scenario. Als we in de toekomst andere lijsten moeten sorteren, zal de test direct weer op rood springen. Vandaar dat we door moeten naar de laatste stap in het TDD-principe. Deze stap heeft helaas geen eigen kleur op het stoplicht, maar is wel de belangrijkste: Refactor.

Fase 3: Refactor (De code optimaliseren)

De refactor-stap is de belangrijkste stap om ervoor te zorgen dat je applicatie uiteindelijk écht naar behoren gaat werken (en dat wil iedereen natuurlijk 😉).

In de refactor-stap zorgen we ervoor dat de code wordt geoptimaliseerd en écht voldoet aan alle acceptatiecriteria. In ons simpele voorbeeld stelt het nu nog niet heel veel voor, maar bij uitgebreidere functionaliteiten die aan veel eisen moeten voldoen, is dit een aardig grote en cruciale stap.

De definitieve code

Als we er nu voor willen zorgen dat onze Sorter altijd en voor elke willekeurige lijst goed descending kan sorteren, moeten we onze functie als volgt aanpassen:

Swift

enum Sorter {    
   static func sort(_ numbers: [Int]) -> [Int] {        
       return numbers.sorted { $1 < $0 }    
   }
}

We gaan hier nu niet te diep in op de Swift-code zelf, maar deze logica zorgt ervoor dat elke lijst die je vanaf nu meegeeft, altijd netjes van hoog naar laag wordt gesorteerd. De test blijft groen, en de code is klaar voor de toekomst!

Ready, test, go!


Test-driven development biedt de mogelijkheid om software te ontwikkelen met de hoogst mogelijke kwaliteit.  Mocht je denken van; “Hey! Deze manier van werken staat mij wel aan." Kijk dan eens op tussen onze vacatures.

Op zoek naar de app ontwikkelaar die samen met jouw de ontwikkeling naar het hoogste niveau kan tillen? Laten we dan eens een kop koffie drinken.

Job Burg

July 29, 2022

Veelgestelde vragen

Wat is test-driven development (TDD)?

Test-driven development is een ontwikkelmethode waarbij eerst testen worden geschreven op basis van vooraf opgestelde acceptatiecriteria. Pas daarna wordt de code ontwikkeld die ervoor zorgt dat deze testen slagen. Zo wordt functionaliteit gebouwd met testen als uitgangspunt.

Waarom zorgt test-driven development voor minder bugs in een app?

Omdat elke functionaliteit vooraf wordt vastgelegd in een test, wordt de code continu automatisch gecontroleerd. Fouten worden daardoor vroeg ontdekt en kunnen nauwelijks ongemerkt in een release terechtkomen.

Is test-driven development duurder dan traditionele ontwikkeling?

In de beginfase kost TDD meer tijd en dus meer investering, omdat eerst testen worden geschreven. Op de lange termijn bespaart het echter tijd en kosten doordat minder handmatig getest hoeft te worden en bugs sneller worden opgelost.

Wat gebeurt er als er toch een bug wordt gevonden?

Wanneer er een bug wordt ontdekt, wordt er eerst een nieuwe test geschreven die deze fout reproduceert. Daarna wordt de code aangepast totdat de test slaagt. Zo wordt voorkomen dat dezelfde bug in de toekomst opnieuw optreedt.

Voor welke projecten is TDD geschikt?

TDD is vooral geschikt voor apps waarbij stabiliteit, betrouwbaarheid en lange termijn onderhoud belangrijk zijn. Denk aan complexe applicaties waar kwaliteit en veiligheid een grote rol spelen.

Gerelateerde blogs