Op de verschillende testconferenties is het agile, model based en test driven wat de klok slaat. Zowel in keynotes als in de tracksessies wordt veelvuldig test driven development (TDD) aangehaald als de methode om fouten te voorkomen. Het idee hierachter is dat het aan de hand van de requirements opstellen van testgevallen ertoe bijdraagt dat die requirements getest worden en de daaruit afgeleide componenten correct zijn. Hierbij komen bij mij een aantal vragen op: is TDD nu testen of specificeren, welke fouten vinden we op deze wijze en wat is het effect op de kosten voor testen binnen het project?
Volgens de theorie is TDD een vorm van specificeren waarbij eerst een geautomatiseerde test wordt opgesteld. Initieel faalt de test natuurlijk omdat er nog geen software is die wordt getest. De ontwikkelaar schrijft vervolgens een component die de gewenste functie uitvoert en biedt deze aan aan de test. Zodra de test foutloos kan worden uitgevoerd is het component gereed (en getest). In de praktijk echter zien we ook dat het testen van specificaties, voordat wordt overgegaan tot de bouw, TDD wordt genoemd. Op beide manieren is wat aan te merken.
Bij de eerste schrijft de ontwikkelaar zijn eigen test met als gevolg dat het mogelijk is dat hij niet de juiste test schrijft. Omdat hij met diezelfde inzichten ook de code schrijft kan het dus gebeuren dat zowel de test als het component op dezelfde foute interpretatie van de specificaties wordt gemaakt en er een juist geprogrammeerde maar foute module ontstaat. Bij de tweede manier worden de testspecificaties gebruikt om aan te tonen dat de requirementsspecificaties correct zijn. Dit is te vergelijken met het bewijzen van een stelling: door het invullen van de parameters bewijzen we dat de stelling klopt. Dit moet overigens niet worden verward met de situatie dat door het bedenken van testgevallen impliciet de specificaties worden getest. Dat is een van de uitgangspunten bij het v-model; in dat geval kom je er als tester 'toevallig' achter wanneer iets niet klopt.
Wat zijn nu de voordelen van TDD? Als belangrijk voordeel wordt genoemd dat het testen verder naar voren in het ontwikkelproject wordt gehaald. Een voordeel is ook dat de fouten worden voorkomen en als ze al worden ingebouwd, deze snel worden gedetecteerd. De voordelen die we kunnen halen zijn afhankelijk van de methode die we toepassen. TDD biedt in beide gevallen een verlengde van de specificatiefase. Zowel bij het maken van de geautomatiseerde test als bij het opstellen van testgevallen wordt het ontwerp in een vroeg stadium verder uitgewerkt. De eerste echter lijkt beter toepasbaar bij agile development, omdat het maken van de geautomatiseerde test op de agile-manier kan plaatsvinden terwijl voor de tweede manier goed uitgewerkte specificaties nodig zijn.
Bij TDD, zoals dat door de ontwikkelaar wordt toegepast, vinden we vooral programmeerfouten. Deze moeten natuurlijk allemaal uit de software verwijderd worden. Het voordeel zit hier vooral in de korte herstelcyclus die optreedt; de programmeur maakt fouten, test, herstelt en test weer net zolang tot alles foutloos verloopt. Een belangrijk neveneffect is dat de programmeur zal leren van de fouten en in volgende situaties steeds minder fouten zal maken waarmee op termijn dus fouten worden voorkomen. We vinden echter niet de fouten die zich openbaren zodra verschillende componenten en systeemdelen geïntegreerd worden tot een systeem. Hiervoor zal dus altijd een vorm van systeemtest en systeemintegratietest noodzakelijk zijn.
De tweede methode is eigenlijk te zien als twee activiteiten tegelijk. In de eerste plaats worden de specificaties verder uitgedetailleerd wat ertoe leidt dat er tijdens de bouw de juiste componenten worden gemaakt. In de tweede plaats worden de testgevallen gemaakt die bij de oplevering worden gebruikt tijdens de test. Bij deze aanpak zit het voordeel er vooral in dat door het opstellen van de testgevallen de requirements verder worden uitgewerkt. Ook hier is sprake van een iteratief proces: de tester wil testgevallen opstellen, vindt onvolledigheden, deze worden opgelost en het maken van testgevallen wordt afgerond. Dit bevordert de juistheid van de specificaties en daarmee het voorkomen van fouten. Voor welke testsoort deze testgevallen bruikbaar zijn hangt af van de requirements die als basis hebben gediend; gebruikersrequirements bijvoorbeeld leveren accepatietestgevallen en systeemrequirements systeemtestgevallen.
Test driven development is zowel specificeren als testen, maar wat levert TDD nu daadwerkelijk op? In de eerste plaats een verbetering van de programmeerkwaliteit en op den duur het voorkomen van fouten. Daarmee is het, in het bijzonder bij de componenttest, een belangrijke verhoging van de kwaliteit. Dit draagt bij aan een besparing op de ontwikkelkosten, voornamelijk door het voorkomen van herstelwerkzaamheden gedurende latere fasen in het project. Bij gestructureerd testen biedt TDD, naast het controleren en verder uitwerken van requirements, de mogelijkheid om zeer vroeg in het project testgevallen op te stellen die later in het project kunnen worden uitgevoerd. Een combinatie van beide methodes is dus optimaal: de ontwikkelaar test zijn code met behulp van TDD door het maken van geautomatiseerde componenttests, de tester beoordeelt de requirements en maakt testgevallen voor de systeem-, systeemintegratie- en acceptatietests en test daarmee de samenhang van de componenten. Daarmee is TDD dus wel degelijk testen.
Wat ik eigenlijk nog een beetje mis is de scheiding tussen tester en ontwikkelaar. In het klassieke verhaal van testen van de specificaties is dit meestal goed vertegenwoordigd. In TDD wordt wel vaker de test en de software door dezelfde persoon geschreven. Dit is eigenlijk onwenselijk maar de traditionele tester heeft vaak niet de vaardigheden om geautomatiseerde tests te schrijven. Toch zie ik graag dat de testen door een ander persoon plaatsvindt dan het ontwikkelen. Dit betekent dus dat er hogere eisen worden gesteld aan de tester of dat dit een mede-ontwikkelaar is.
Dit artikel maakt onderscheid tussen TDD door ontwikkelaars en TDD door testers.
Het eerste, TTD door ontwikkelaars, heeft meer met ontwerp te maken dan met testen of specificeren. De ontwikkelaar ontwerpt de programmeer interface van zijn componenten of klasses door TDD. Dat gebeurt onafhankelijk van het testen door de tester.
Deze tests zijn een enabler voor het refactoren van de programma code en zorgen voor onderhoudbare code en het versnellen van feedback (eerder vinden van fouten, waar in de praktijk de meeste tijd in gaat zitten).
Het automatiseren van systeemtesten en integratie testen door testers is een aparte uitdaging en heeft weinig met het testen door ontwikkelaars te maken. Het vergt aparte skills, die bij een technisch tester rol horen. Ontwikkelaars kunnen daarbij wel helpen, bij voorkeur andere ontwikkelaars dan zij die de code ontwikkelen.
Als aanvuling is er ook Acceptatie-TDD , of Agile Acceptance Testing. Dat gebeurt vooral in samenwerking tussen klant, business analist, tester, ?n ontwikkelaars. Hierin worden vooral specificaties opgesteld, veelal in de vorm van concrete voorbeelden. Ook deze zijn te automatiseren en kunnen de rol van documentatie en regressie test suite vervullen.
Richard Gremmen
Software ontwikkelaar bij Hot ITem in Amsterdam
Chris Schotanus vraagt zich af: “Test driven development: is dat testen of specificeren?”. Jammer dat hij op deze vraag het foute antwoord “allebei” geeft. Het goede antwoord is: geen van beide! Test-driven development is primair een vorm van programmeren. Kent Beck schrijft in “Test-Driven Development By Example” (ISBN 0-321-14653-0): “In Test-Driven Development, we:
– Write new code only if an automated test has failed
– Eliminate duplication”
Beck legt uit dat een programmeur door TDD toe te passen de ontwikkeling van code stuurt met behulp van geautomatiseerde tests. TDD bestaat namelijk uit korte cycli waarbinnen de programmeur een (1) geautomatiseerde test maakt, de tests draait om te constateren dat de nieuwe test inderdaad faalt, code toevoegt om de test te laten slagen en vervolgens eventuele duplicatie verwijdert. Een zo’n cyclus duurt hoogstens een paar minuten.
TDD heeft als gunstig neveneffect dat een verzameling geautomatiseerde unittests ontstaat die zo vaak gedraaid kan worden als nodig. Maar, dat maakt het nog geen testen. TDD is een methode voor programmeurs om beter te programmeren.
Frank Niessink
Principal consultant bij M&I/Partners
Frank Niessink haalt twee zaken door elkaar: functies en rollen. Natuurlijk is TDD bedoeld om de programmeur te helpen de programma’s beter de ontwerpen. Maar, zoals Beck zeg: de code moet worden aangepast als de geautomatiseerde test faalt. Dat houdt in, dat de programmeur de rol vam tester moet aannemen om eerst de tests te schrijven. Deze test blijft vervolgens referentie voor de kwalitit van de componenten.
Daar komt ook de terechte opmerking van Ben Ootjers bij: testen is een apart vak en testers kijken anders naar requierements dan ontwikkelaars.
Overigens heeft deze vorm van TDD alleen effect op de kwaliteit van de individuele componeneten. Dus slaat Richard Gremmen de spijker op z’n kop dat het (al dan niet geautomatiseerd) testen gedurende systeem- en systeemintegratietest een vak apart is en, hoe goed de individuele componeneten ook zijn geprogrammeerd, bijzonder noodzakelijk zijn en blijven. Dit ondanks het feit dat we TDD principes ook toepassen op de requirements: maak eerst de testgevallen ga dan pas verder met de systeemontwikkeling.
Uit het artikel blijkt dus dat het niet uitmaakt of je met TDD nu specificeert of toch test. Belangrijk(er) is dat met deze werkwijze de feedbackloop verkort wordt; een principe dat belangrijk is in een Agile omgeving.
Het uiteindelijke gevolg hiervan is dat de productkwaliteit verbeterd wordt; iets waar in een agile team het hele team verantwoordelijk voor is. Een rol- of functiescheiding is van ondergeschikt belang in een Agile omgeving.
Natuurlijk zullen, zoals Ben Oojters aanhaalt, aan zowel testers als ontwikkelaars hogere eisen gesteld worden, maar dat is al evident aan het ontwikkelen in een agile omgeving en niet alleen aan het ontwikkelen volgens TDD.
Veel woorden. We kunnen over het onderwerp test driven development heel kort zijn: het is geen specificeren, het is geen testen, het is simpelweg development, uitgevoerd door developers.
Test driven development is simpelweg de hippe (en blijkbaar licht verwarrende) term voor wat bedenker Kent Beck vroeger gewoon unit testing noemde.
In mijn optiek omvat TDD zowel unit testen als acceptatie testen. Ik zou TDD dan ook willen definieren als “het uitwerken van testgevallen op basis van een specificatie voordat de productiecode wordt geprogrammeerd”. Je schrijft dus tests voordat je programmeert. Basta.
Terecht constateert Chris dat je daarmee en betere specificaties en betere code krijgt. Dat staat onomstotelijk vast. Dus ieder project wat deze practice nog niet uitvoert is verkeerd bezig: zonder TDD is je code van matige kwaliteit en je specificaties zullen door gebrek aan detail regelmatig herzien moeten worden.
Het inzicht wat ik mis is dat binnen de agile aanpak beide varianten van TDD uitermate efficient gecombineerd kunnen worden: Schrijf bij iedere user story samen met de klant de acceptatietest gevallen van die user story en schrijf unittests voordat je codeert. Op die manier is de kans dat de code nog interpretatie- of codeerfouten bevat fors geminimaliseerd. Door de acceptatietestgevallen beschikbaar te hebben bij de code (wij loggen zowel de user stories als de bijbehorende testgevallen in JIRA) heb je een zeer korte feedback loop en is het dus goedkoper om eventuele fouten te herstellen.
Wij moeten er vanaf dat testers achteraf testgevallen gaan uitvoeren: specificeer je testgevallen vooraf en werk nauw samen met de ontwerpers en ontwikkelaars om de implementatie ervan te valideren.
Anko Tijman
Computable Expert topic Development
Principal Consultant Agile Testen