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 dit 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. Je start met het analyseren van alle requirements die er zijn voor de app. Vervolgens stel je acceptatiecriteria op, waaraan voldaan moet worden om de app succesvol op te leveren. 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. 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
Zodra we over TDD praten, wordt vaak de metafoor of vergelijking aangehaald van een stoplicht. Wij zullen je even meenemen waarom de metafoor van het stoplicht zo goed past bij deze methodiek. Zoals iedereen weet heeft een stoplicht drie kleuren; groen, oranje en rood. Elke kleur heeft een betekenis in het verkeer.
? Gas geven! Hier mag je lekker doorrijden, jij hebt voorrang voor iedereen.
? Ga ik remmen of geef ik wat gas bij? Dit is de vraag die iedereen zich stelt als die het stoplicht op oranje ziet gaan. Maar volgens de regels moet je stoppen als je dit nog kan.
? Remmen! Rood betekent dat je moet stoppen en niet mag doorrijden.
Ik neem aan dat deze memory refresher voor niemand nodig was, maar hij helpt wel heel goed bij het uitleggen van het metafoor. Want bij test-driven development doen we eigenlijk precies hetzelfde alleen betekent elk kleurtje weer net iets anders en hebben we bij TDD niet de kleur oranje, maar is het net wat anders.
? Dit is de kleur die je als eerste zult zien als je de test gaat schrijven. Dit komt doordat je altijd begint met testen en dan pas code gaat schrijven. Je maakt als het ware een blueprint van wat je gaat testen en daarna ga je dit invullen, net als een architect eerst een tekening maakt en dan pas het daadwerkelijk gaat bouwen. En het wordt nog mooier, er mag pas gebouwd worden als de blauw print is goedgekeurd (daar zijn ze weer: de acceptatiecriteria).
Omdat je in het begin nog geen code hebt geschreven weet de compiler niet wat het ziet en gaat die dus foutmeldingen teruggeven. Bij een foutmelding is de test niet succesvol en betekent het dus dat die faalt. Rood je mag hier niet verder.
? Zodra je de blueprint van de test hebt geschreven, ga je ervoor zorgen dat rood veranderd naar groen. Dit doe je door er met zo min mogelijk code voor te zorgen dat de test wel draait. Hieronder een voorbeeld.??
Eerst maken we onze test aan met een test erin. Deze test is heel simpel, we hebben een lijst van nummers en deze nummers willen we van hoog naar laag gaan sorteren. Sounds easy right?
class TDDBlogTests: XCTestCase { func testSimpleSortingDesc() { let numbers = [5, 6, 3, 2, 1] XCTAssertEqual([6, 5, 3, 2, 1], Sorter.sortDesc(numbers)) } } |
Wat we dan volgens deze methodiek doen, is nu er alleen voor zorgen dat deze test groen wordt, zonder verder te kijken naar de acceptatiecriteria. Hiervoor moeten we eerst een Sorter aanmaken met een functie die sortDesc heet, die een lijst van nummers accepteert. Wat je hieronder dus ziet is dat we de lijst in de goede volgorde teruggeven.
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 teruggegeven zoals we hem verwachten. Dit werkt nu alleen voor dit specifieke scenario. Als we in de toekomst andere lijsten moeten sorteren zal de test dus weer op rood springen. Vandaar de laatste stap in het stoplicht principe, wat helaas geen kleur heeft, namelijk refactor.
Nu de laatste en de belangrijkste stap om ervoor te zorgen dat je app uiteindelijk naar behoren gaat werken (dit wil iedereen natuurlijk ;-)). Namelijk de refactor stap. In de refactor stap zorgen we ervoor dat de code geoptimaliseerd is en voldoet aan de acceptatiecriteria. In het simpele voorbeeld van hierboven, stelt het natuurlijk niet heel veel voor, maar bij uitgebreidere functionaliteiten die aan veel eisen moeten voldoen, is dit een aardig grote stap. Als we er nu voor willen zorgen dat onze Sorter altijd goed descending kan sorteren zullen we onze functie als volgt moeten aanpassen.
enum Sorter { static func sort(_ numbers: [Int]) -> [Int] { return numbers.sorted { $1 < $0 } } } |
We gaan niet verder in op de code, maar dit zorgt ervoor dat elke lijst die je ook meegeeft altijd van hoog naar laag terugkomt.
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.
Neem contact op
Wil jij een app laten maken of graag meer weten? Neem vrijblijvend contact op en ik help je graag verder.
CONTACT
Heb je een vraag? Neem vrijblijvend contact op en ik help je graag verder.