Tag unit tests

Tests zijn specs

Kleine (of liever: te kleine) tests geven weinig informatie over de werking van het systeem. Ze verlangen van de lezer om op de hoogte te zijn van implementatiedetails, en verliezen zo het grote geheel uit het zicht. Voor grotere unittests gaat die beperking niet op. Zulke tests doen geen aannames over de interne werking van het systeem. Ze beschrijven slechts de buitenkant ervan: wat de gebruiker - hoe we die dan ook mogen definiëren - invoert en wat deze terugkrijgt. Het gevolg daarvan is dat je tests leesbaar worden voor niet-ontwikkelaars.

Over de volgorde van je unit tests

Maakt het uit in welke volgorde je unit tests staan? - Nee. Tests slechts een middel om de werking van het systeem te valideren. Het maakt niet uit in welke volgorde de tests worden afgetrapt, als ze maar allemaal slagen. (Of liever: als ze maar op het juiste moment falen.) Dat is een mogelijk antwoord. - In een praatje van Kevlin Henney vond ik een ander antwoord.

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!

Wat is een unit?

Een tijd geleden presenteerde ik enkele ideeën over testen aan mijn collega’s. Toen ik aankwam bij het gedeelte waarin ik stelde je het best via de voordeur kunt testen, stelde een collega apodictisch: “Een unittest test een openbare method op een class. Elke afhankelijkheid op die class moet je mocken.” Dat is een manier om er naar te kijken, zeker. Het is de zogenaamde mockist strategie, ook wel bekend als de Londense school van unittesten. De vraag waar ik me op wil richten is: waar komt de aantrekkingskracht van deze strategie dan vandaan? Ik geloof dat deze voortkomt een bepaalde interpretatie van wat een unittest is of moet zijn. Meer specifiek: wat de unit in unittest is of moet zijn.

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!

Tests als vangnet

Tests zijn een vangnet. Elke keer dat je code aanraakt - en dat doe je continu -, dan speel je een balanceeract. Als je code blijft functioneren zoals bedoeld, blijf je op het koord. Zo niet, dan val je. En als je valt, heb je een keus: te pletter vallen, of opgevangen worden door een vangnet. Tests zijn je vangnet, je afgrond is - ontevreden ontwikkelaars, stakeholders, eindgebruikers.

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.

Agile en Test-Driven Development

De meeste ontwikkelaars (waaronder ondergetekende!) schrijven als volgt code. Ze bekijken de specificaties en beginnen vervolgens te klungelen. Dat duurt een tijd, totdat ze iets hebben wat werkt. Of dat zo is, verifiëren ze middels handmatige tests. Pas als de code werkt als bedoeld, schrijft men - als het goed is! - een reeks geautomatiseerde tests. Het is een hardnekkig misverstand dat TDD deze praktijk van software schrijven omdraait: eerst de geautomatiseerde tests (meervoud!) schrijven, en dan pas de productiecode. De werkelijkheid ligt wat genuanceerder.

De leercurve van Angulartests beklimmen - deel 4

Wat is de kern van ons onderhoudprobleem? Codeduplicatie - of liever: de duplicatie van informatie. Het opzetten van een bepaalde service en haar afhankelijkheden gebeurt voor elke (reeks) test(en) handmatig in de beforeEach-methode. Als dezelfde afhankelijkheid in twee verschillende reeks testen voorkomt, moet de ontwikkelaar deze twee keer uitschrijven. Maar is dat nu echt nodig?