Unit Testing - Hoofdstuk 11

Ingediend door Dirk Hornstra op 28-mar-2022 19:34

Het backend-overleg is "pas" volgende week, maar laat ik de samenvatting eens mooi op tijd delen! Na hoofdstuk 1: link, hoofdstuk 2: link, hoofdstuk 3: link, hoofdstuk 4: link, hoofdstuk 5: link, hoofdstuk 6: link, hoofdstuk 7: link, hoofdstuk 8: link, hoofdstuk 9: link en hoofdstuk 10: link is het nu tijd voor hoofdstuk 11.

De Timo-samenvatting

Maak private functies niet public omdat ze anders niet te testen zijn. Hou je testen simpel, als je code gaat kopiëren uit de code die je gaat testen dan doe je het niet goed. Hou productie-code schoon, voeg geen checks toe of je "de code aan het testen bent" om bepaalde acties uit te sluiten/over te slaan. Maak geen mock-object van een concrete class, alleen van interfaces.

En dan nu het complete verhaal:

Oh, we zijn bij het laatste hoofdstuk gekomen! Dit hoofdstuk heeft de titel "Unit testing anti-patterns" en dat zegt op zich wel voldoende. Een anti-pattern is een algemene oplossing die op het eerste gezicht een prima oplossing biedt, maar bij langer gebruik je problemen op gaat leveren.

Het testen van "private methods".

Korte antwoord: moet je eigenlijk niet doen. En dan nu het langere antwoord:

Als je private methods publiekelijk toegankelijk gaat maken zodat je ze kunt testen, daarmee schend je het principe uit hoofdstuk 5, alleen waarneembare zaken ga je testen. Door het op deze manier te doen ga je jouw testcode koppelen aan de implementatie, waardoor het refactoren van code moeilijker gemaakt wordt.

Soms is code in functies "te complex". En de omvattende code die wel te testen is, die biedt niet voldoende tests van deze functionaliteit. Volgens het boek heb je dan 2 mogelijkheden:

  • Het is eigenlijk "dode code", overgebleven na refactoring. Deze zou je dus moeten verwijderen.
  • Als de boel écht te complex is, heb je waarschijnlijk een abstractie niet goed uitgevoerd en zou deze code over moeten naar een eigen class.


Het boek laat een voorbeeld zien van "GetPrice" waar binnen een hele complexe rekenmethode wordt uitgevoerd. Hier wordt een eigen class "PriceCalculator" voor gemaakt die prima "los" te testen is.

In sommige gevallen wil je private methods wel testen. Het boek geeft een voorbeeld van een systeem van aanvragen. Deze worden uit een database gerestored en stuk voor stuk "Approved" (of Denied). De constructor is private, omdat het ORM dat doet, maar als je wilt gaan testen heb je een public constructor nodig. In dit geval mag/kun je die constructor public maken.

We zien vervolgens een class Customer met een private veld status. Door een "Promote()" functie aan te roepen kun je de status aanpassen. De functie "Discount" geeft afhankelijk van de status je een normale korting (0%) of VIP-korting (5%). Je moet dus niet dit private veld public maken, je moet in je test deze validaties gaan inbouwen.

Het lekken van "domein-kennis" naar de test

We zien het voorbeeld van een functie Add(x, y) die getest wordt. Het verwachte getal wordt binnen de test vergelijkbaar berekend als in de functie. Dan heeft het testen geen nut. Hier is de "berekening/werking" gelekt naar de test. Je gaat "harde getallen" met elkaar vergelijken. Dus Assert.Equal(Add(1,3),4).

Code-vervuiling

Het toevoegen van code aan productie-code om test-zaken mogelijk te maken.

We zien het voorbeeld van een Logger-class. Bij de constructor kun je een bool testMode mee geven. Als die actief is wordt er niets gelogd. Maar nu vermeng je dus het testen van je code met de "echte" productie-code. Maak van de class een Interface en maak er een eigen implementatie van die niets logt. Die ga je dan gebruiken voor het testen, zo vervuil je niet jouw productie-code met allemaal checks en uitzonderingen om test-acties "niets te laten doen".

Mocken van concrete classes

Het boek heeft het mocken met interfaces getoond, maar je kunt dit ook met concrete classes doen. Hierdoor behoud je een deel van de functionaliteit van de class. Maar je overtreedt hiermee het "single responsibility-principe".

We krijgen een voorbeeld van een class StatisticsCalculator. Die doet berekeningen, maar vraagt ook leveringen op bij een externe dienst. De class doet dus eigenlijk 2 dingen, 1 te veel. Het boek laat zien hoe je de communicatie via een andere class kunt laten lopen, die gebaseerd is op een Interface. En die kun je dan zelf weer mocken, zodat je zonder problemen de StatisticsCalculator kunt testen.

Het werken met tijd

Soms zijn er afhankelijkheden tijdens het testen met datum en tijd. En kunnen daardoor false positives optreden. Het boek geeft een aantal opties.

Tijd als "ambient context". Je gebruikt niet de DateTime.Now, maar een eigen class. We zien een voorbeeld en dus hoe het niet moet. Je maakt weer afhankelijkheden, omdat er gebruik wordt gemaakt van een static variabele maak je dat testen elkaar kunnen beïnvloeden.

Tijd als "expliciete afhankelijkheid (dependency)". Hiermee geeft je de tijd door als een service of als een vaste waarde. Waarbij een vaste waarde de voorkeur geniet.

 

Wil je meer van de auteur weten of het blog willen volgen van dit boek, hier na te lezen: link blog, twitter: link.