Tag test-driven development

Wij van TDD-eend...

Het is waar: uit het feit dat er tests zijn, valt niet te concluderen dat het systeem functioneert zoals verwacht - preciezer: zoals de eindgebruiker verwacht. Er valt hooguit uit te concluderen dat het systeem functioneert zoals de schrijver van de tests verwachtte. En als de schrijver van de tests tevens de ontwikkelaar van de code is, dan vormen de tests niet meer dan een verslaglegging van de aannames die de ontwikkelaar had tijdens de implementatie. En tóch geloof ik dat TDD het probleem van mijn collega - mijn feature voldoet niet aan de (impliciete?) requirements - had kunnen verhelpen.

Test het systeem, niet de class

Het is belangrijk om vast te stellen dat er een bug in het systeem was geslopen, ondanks dat de functionaliteit die de bug veroorzaakte ogenschijnlijk gedekt was door tests. Waarom “ogenschijnlijk”? De class die de serialisatie voor zijn rekening nam, werd wel getest, maar alleen in isolatie en niet in de context van het systeem. - Vraag je af wat de implicatie daarvan is. Het betekent dat onze tests bewezen dat een class naar behoren werkt. Of het systeem als geheel naar behoren werkt, dat kunnen we op basis van de tests niet concluderen. Terwijl dat juist is waar het om gaat!

De fix die productie om zeep hielp

“De fix staat op productie,” meldden we trots. “Het bleek te liggen aan wat validatielogica die in dit specifieke scenario wat te streng bleek - enfin, een technisch verhaal, dat hoef je niet te weten. Het enige wat je hoeft te weten is: we hebben weer eens geweldig werk geleverd, bedankjes worden gewaardeerd maar zijn niet noodzakelijk.” En we leefden nog lang en gelukkig. Een halfuur lang. Want toen kregen we een berichtje: “Help! Ik kan nu geen enkel item meer inzien!”

Tests als ontwerpmiddel

Tests zijn een ontwerpmiddel, een design tool. Ze fungeren als indicator voor de kwaliteit van het ontwerp van je code. De vuistregel is even eenvoudig als zag-het-niet-want-het-stond-recht-voor-mijn-neus-vanzelfsprekend: Is het moeilijk om er een test voor te schrijven? Dan deugt het ontwerp niet!

Test-driven development is een ontwerpdiscipline

Een collega benaderde me laatst met een vraag over een stuk code. Hij was bezig met het implementeren van een feature om een toets met afbeeldingen om te zetten naar een PDF-representatie ervan. Zijn vraag was: hoe kom ik aan die afbeelding? Of liever: waar kom ik aan die afbeelding? Zijn eerste ingeving was om via dependency injection de relevante repository mee te geven aan de ImagePdfGenerator. - Het is een klassiek geval van het verknopen van het ophalen van data en het manipuleren ervan. Ik bleef wel met een knagend gevoel achter: hoe makkelijk blijkt het om dankzij een DI-container de verantwoordelijkheden van classes te verwateren - en wat moeten we daarmee?

Zelfs de testpiramide is niet meer heilig!

Ik ben altijd in de veronderstelling geweest dat mijn geautomatiseerde tests een piramidevormige verhouding tot elkaar zouden moeten hebben: aan de basis enorm veel unittests, in het midden een goede hoeveelheid integratietests, en aan de top een bescheiden aantal end to end (E2E) tests. Totdat ik Learning Domain-Driven Design van Vlad Khononov las. (Een aanrader, overigens!)

Testen via de voordeur

Het ideaal van Test-Driven Development is dit: al coderend breid je je testsuite uit, zonder ooit één test te hoeven herschrijven. Het toevoegen van nieuwe functionaliteit gebeurt op zijn best binnen de kaders van een bestaande API. Op zijn slechtst leidt het tot uitbreidingen daarvan - meer niet. Er is, denk ik, een manier om dichter bij dat ideaal te komen. Dat ideaal bereik je door te testen via de voordeur. Dat houdt kort gezegd in dat je je logica idealiter test via dezelfde route als een gebruiker van je code.

Mijn eerste testgedreven stapjes

Na niet één, niet twee, niet drie, niet vier, maar vijf blogs over Test-Driven Development had ik ervoor nodig, voordat ik het aandurfde. Maar nu is het dan eindelijk zover: onlangs heb ik de eerste testgedreven stapjes gezet in mijn professionele carrière. Een mijlpaal!

Nóg een reden om testgedreven te ontwikkelen

Als je mij zou vragen: waarom zou je testgedreven ontwikkelen? dan zou ik zeggen: zodat je tests hebt. Maar in The Art of Agile Development van James Shore vond ik een andere reden. Test-Driven Development dwingt je na te denken hoe het is een stuk code te gebruiken, in plaats van het te implementeren. Het vraagt je om helder te krijgen: hoe kan ik deze functionaliteit zo goed mogelijk ontsluiten, in plaats van: hoe kan ik deze functionalteit zo goed mogelijk bouwen?

Legacy code en Test-Driven Development

TDD gaat over het toevoegen van nieuwe features - per definitie. Immers, wie tests toevoegt voor bestaande code is niet test-driven aan het developen. Maar de meeste ontwikkelaars werken helemaal niet aan greenfield-applicaties. Ze slepen zich dag na dag, week na week door het moeras dat we legacy code noemen. TDD lijkt te zijn weggelegd voor de lucky few onder ons die nieuwe applicaties mogen ontwikkelen. De rest van ons mag ploeteren in bestaande drek. Maar die conclusie gaat toch niet helemaal op.