Home » Begrippen » Wat is een unit test?

Wat is een unit test?

Een unit test is een geautomatiseerde test die één afzonderlijke functie, methode of module van een applicatie isoleert en controleert of die precies het verwachte gedrag vertoont. Het woord “unit” verwijst naar de kleinste testbare eenheid van code: doorgaans een enkele functie of klasse. Zo test een unit test voor een functie die BTW berekent of de uitkomst bij een gegeven bedrag en percentage correct is, ongeacht de rest van de applicatie. Een unit test voor een wachtwoordvalidatiefunctie controleert of te korte wachtwoorden worden afgewezen en sterke wachtwoorden worden geaccepteerd. Unit tests vormen de basis van de testpyramide en zijn de snel uitvoerbare, nauwkeurige signalering waarmee ontwikkelaars direct feedback krijgen over de correctheid van hun code. Ze zijn onmisbaar in moderne softwareontwikkeling en vormen de ruggengraat van vrijwel elke CI/CD-pipeline.

Wat maakt een goede unit test?

Niet elke test die “unit test” heet, is ook werkelijk een goede unit test. Er zijn een aantal kenmerken waaraan een kwalitatieve unit test voldoet.

Isolatie

Een unit test test één ding en één ding alleen. Als de functie die je test afhankelijk is van een database, een externe API of een andere module, dan mock je die afhankelijkheden. Op die manier weet je zeker dat een falende test wijst op een probleem in de te testen functie en niet in een externe afhankelijkheid.

Snelheid

Unit tests moeten razendsnel zijn. Omdat een goede testsuit honderden tot duizenden unit tests kan bevatten, moeten ze in totaal in seconden kunnen worden uitgevoerd. Dit maakt het mogelijk om ze bij elke codewijziging te draaien zonder de ontwikkelaar te vertragen.

Herhaalbaarheid

Een unit test moet bij elke uitvoering exact hetzelfde resultaat geven, ongeacht het tijdstip, de omgeving of de volgorde van uitvoering. Tests die soms slagen en soms falen zonder codewijziging zijn “flaky” en ondermijnen het vertrouwen in de testsuite.

Duidelijkheid

Wanneer een test faalt, moet de foutmelding duidelijk maken wat er fout is. Goede testnamen beschrijven precies wat er wordt getest en welk resultaat wordt verwacht, zodat je zonder de code te lezen begrijpt wat er misgaat.

De structuur van een unit test: AAA-patroon

Het AAA-patroon (Arrange, Act, Assert) is de standaardstructuur voor het schrijven van unit tests en maakt tests overzichtelijk en leesbaar.

  • Arrange: Zet de testomgeving op. Maak de benodigde objecten aan, configureer mocks en stel de beginwaarden in.
  • Act: Roep de functie of methode aan die je wilt testen met de voorbereide invoer.
  • Assert: Controleer of de uitkomst overeenkomt met wat je verwacht. Als de verwachte waarde niet overeenkomt met de werkelijke waarde, mislukt de test.

Dit patroon zorgt voor een duidelijke scheiding van verantwoordelijkheden binnen de test en maakt het makkelijker om te begrijpen wat er wordt getest wanneer je de code later teruglesst.

Mocking en stubbing in unit tests

Omdat unit tests in isolatie draaien, heb je tools nodig om afhankelijkheden te vervangen door gesimuleerde versies. Hier komen mocks en stubs in beeld.

Een stub is een vereenvoudigde vervanging van een afhankelijkheid die een vooraf bepaalde waarde retourneert. Stel dat jouw functie een externe weerservice aanroept: in de unit test vervang je die service door een stub die altijd “25 graden” retourneert, zodat je kunt testen hoe jouw functie met die uitkomst omgaat.

Een mock gaat een stap verder: het verifieert ook of bepaalde methoden daadwerkelijk zijn aangeroepen, met welke argumenten en hoe vaak. Dit is handig om te testen of jouw code correct communiceert met afhankelijkheden.

Populaire mocking-bibliotheken zijn Jest (JavaScript/TypeScript), Mockito (Java) en unittest.mock (Python).

Populaire frameworks voor unit tests

Er zijn voor elke gangbare programmeertaal uitstekende testframeworks beschikbaar:

  • Jest: Het meest gebruikte testframework voor JavaScript en TypeScript. Bevat ingebouwde mocking, code coverage en een prettige syntax.
  • Vitest: Een snel, modern alternatief voor Jest dat naadloos integreert met Vite-projecten.
  • PHPUnit: De standaard voor PHP, breed ondersteund in frameworks als Laravel en Symfony.
  • JUnit: Het klassieke en meest gebruikte framework voor Java, ook gebruikt in Kotlin-projecten.
  • Pytest: Het populairste testframework voor Python, bekend om zijn eenvoud en uitbreidbaarheidmogelijkheden via plugins.
  • NUnit / xUnit: Testframeworks voor het .NET-ecosysteem (C#).

Unit tests en Test-Driven Development

Unit tests spelen een centrale rol in Test-Driven Development (TDD): een ontwikkelmethodologie waarbij je eerst de test schrijft voordat je de bijbehorende productiecode schrijft. De cyclus verloopt in drie stappen, ook wel “red-green-refactor” genoemd:

  1. Red: Schrijf een test die faalt omdat de functionaliteit nog niet bestaat.
  2. Green: Schrijf de minimale productiecode om de test te laten slagen.
  3. Refactor: Verbeter de code zonder de tests te breken.

TDD leidt van nature tot een hoge testdekking en dwingt je na te denken over het gewenste gedrag van je code voordat je begint te implementeren, wat resulteert in beter ontworpen code.

Conclusie

Unit tests zijn de hoeksteen van betrouwbare softwareontwikkeling. Ze geven je de zekerheid dat individuele stukken code correct werken, ze signaleren regressies direct wanneer er iets verandert, en ze geven ontwikkelaars de vrijheid om met vertrouwen te refactoren en nieuwe functies toe te voegen. Een goede suite van unit tests is geïsoleerd, snel, herhaalbaar en duidelijk in zijn foutmeldingen. Gecombineerd met integratietests en E2E-tests vormen unit tests de testpyramide die jouw applicatie tegen regressies beschermt. Begin vandaag met het schrijven van unit tests voor de kritieke logica in jouw applicatie en maak het een gewoonte om nieuwe functies altijd te voorzien van bijbehorende tests.

Veelgestelde vragen

  1. Wat is het verschil tussen een unit test en een integratietest?
    Een unit test test één functie of module in isolatie, waarbij alle externe afhankelijkheden worden gemockt. Een integratietest test hoe meerdere modules of componenten samenwerken, inclusief echte afhankelijkheden zoals een database of een externe service. Unit tests zijn sneller en preciezer; integratietests zijn langzamer maar testen het samenspel van componenten.
  2. Hoeveel unit tests moet ik schrijven?
    Er is geen universeel getal, maar een goede vuistregel is om alle publieke functies en methoden te testen, met bijzondere aandacht voor randgevallen en foutsituaties. Streef naar een hoge testdekking voor kritieke bedrijfslogica, maar let op kwaliteit: tien goed geschreven tests zijn waardevoller dan vijftig oppervlakkige tests.
  3. Moet ik private functies unit testen?
    Over het algemeen test je private functies indirect via de publieke interface van de klasse of module. Private implementatiedetails zijn bewust verborgen en kunnen vrij veranderen; als je ze direct test, koppel je je tests te sterk aan de implementatie, wat refactoring bemoeilijkt. Als een private functie zo complex is dat je haar direct wilt testen, overweeg dan of ze beter als een aparte, publieke functie kan worden geëxtraheerd.
  4. Wat is code coverage en hoeveel procent is genoeg?
    Code coverage meet welk percentage van de broncode door tests wordt uitgevoerd. Een hogere coverage geeft meer zekerheid, maar honderd procent is vaak niet haalbaar en ook niet altijd zinvol. Focus op de kritieke bedrijfslogica en richt je op zeventig tot negentig procent coverage voor units met complexe logica. Kwaliteit van tests telt meer dan het percentage.
  5. Hoe ga ik om met legacy code die geen unit tests heeft?
    Begin met het schrijven van zogeheten “characterization tests”: tests die het huidige gedrag van de code vastleggen zonder te oordelen of dat gedrag correct is. Zo bescherm je jezelf tijdens refactoring. Voeg daarna tests toe terwijl je nieuwe functies bouwt of bugs oplost, zodat de testdekking geleidelijk toeneemt zonder dat je de hele bestaande code in één keer hoeft te herschrijven.

Al onze begrippen

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0-9