Testing
Testen van applicaties gebeurt op verschillende niveaus. Hoewel niet iedereen dezelfde niveaus van elkaar onderscheidt, maakt men in het algemeen een onderscheid tussen unit testing en end-to-end testing.
Unit testing omvat het testen van individuele onderdelen van de code, zoals functies of methoden. Meestal wordt hier een white-box principe gehanteerd: de tester kent de inhoud van de unit en mag code schrijven die gebruik maakt van deze kennis. Typische frameworks voor unit testing van Express applicaties zijn Mocha en Jest.
End-to-end testing omvat het testen "zoals een gebruiker". Deze vorm volgt het black-box principe. In essentie omvat dit het automatiseren van volledige browserinteracties. Typische frameworks zijn Cypress of Selenium.
Jest
Jest is een testframework dat origineel ontwikkeld werd door Facebook. Het is een van de meest populaire testframeworks voor JavaScript. Jest is een all-in-one oplossing die zowel de testrunner als de assertion library bevat. Jest is zeer eenvoudig in gebruik en heeft een goede documentatie.
Installatie
Om Jest te installeren, voer je volgend commando uit:
Configuratie
Om Jest te kunnen gebruiken (met TypeScript), voer je dit commando uit:
Om te zorgen dat je al je Jest-tests kan laten lopen met npm test, voeg je dit toe aan package.json:
Node.js testen
Basis
Om een bepaalde functie te kunnen testen, moet je deze functie exporteren. Daarom is het belangrijk om zoveel mogelijk modules te gebruiken die je kan exporteren.
Stel dat je een functie hebt die een string omzet naar hoofdletters in een bestand string-utils.ts
:
Om deze functie te testen, maak je een bestand string-utils.test.ts
:
it
is een functie die een test definieert. De eerste parameter is een beschrijving van de test, de tweede parameter is een functie die de test uitvoert. Je kan ook test
gebruiken in plaats van it
.
We kunnen nu de tests uitvoeren met npm test
. We krijgen dan volgende output:
Jammer genoeg is hier de tester hier niet in geslaagd om de bug te vinden. De functie toUpperFunction
is namelijk niet correct. Als de input speciale tekens bevat zoals de duitse karacters met umlauten, dan zal de functie deze niet omzetten naar hoofdletters. De volgende test zou dit kunnen aantonen:
Deze test zal falen. De correcte implementatie van de functie zou zijn:
Exceptions
Als je een functie hebt die een exception kan gooien, kan je dit testen met toThrow
:
We kunnen deze nu testen met:
Let op dat we hier een arrow functie gebruiken om de functie calculateSquareRoot
op te roepen. Dit is nodig omdat we anders de exception niet zouden kunnen opvangen en de test zou falen.
Asynchronous code
Als je een functie hebt die asynchroon werkt, kan je dit testen met async
en await
:
We kunnen deze nu testen met:
Test setup en teardown
Als je bepaalde code wil uitvoeren voor en na elke test, kan je dit doen met beforeEach
, afterEach
, beforeAll
en afterAll
. Deze kunnen zich in de describe
blokken bevinden of globaal in het bestand.
Dit wordt gebruikt om bijvoorbeeld een database connectie te openen en te sluiten voor en na elke test.
Express testen
Als we een Express applicatie willen testen, kunnen we gebruik maken van de supertest
library. Deze library maakt het mogelijk om HTTP requests te versturen naar een Express applicatie en de response te testen.
We moeten deze dan ook nog installeren:
Stel dat we een Express applicatie hebben die een GET request afhandelt op de route /hello
:
Let wel op dat we nu wel de app moeten exporteren. Dit is nodig om de app te kunnen testen.
We kunnen deze nu testen met:
Als je deze test nu uitvoert met npm test
, dan krijg je de volgende error:
Om dit op te lossen kunnen we de app code in een apart bestand zetten en de code in index.ts
aanpassen:
en de rest van de code in app.ts
:
Query parameters
Als je een route hebt die query parameters verwacht, kan je deze testen met:
en de test:
POST requests
Als je een route hebt die POST requests afhandelt, kan je deze testen met:
en de test:
HTML responses
Als je een route hebt die HTML responses teruggeeft, kan je deze testen met:
en de test:
of je kan de HTML parsen met node-html-parser
en dan de inhoud van de h1 tag testen:
Coverage
Jest kan ook gebruikt worden om de code coverage te berekenen. Dit is het percentage van de code dat door de tests gedekt wordt. Hoe hoger dit percentage, hoe beter je code getest is. Eerst moet je wel in je package.json
de volgende lijn toevoegen bij de scripts.
Nu kan je de coverage berekenen met npm run coverage
. Je krijgt dan een overzicht van de coverage van je code.
Je krijgt een uitgebreid overzicht van welke lijnen er wel en niet getest zijn. Dit kan je helpen om te zien welke delen van je code nog niet getest zijn en waar je nog extra tests moet schrijven. Je kan dit verslag vinden in de map coverage/lcov-report/index.html
.
Mocking
Unit testen wordt vaak lastiger wanneer je code interageert met "de buitenwereld": filesystemen, databanken, invoer van de gebruiker, uitvoer naar de terminal, externe servers,...
Om deze reden wordt vaak gebruik gemaakt van "mocks": waarden die de plaats innemen van onderdelen die het moeilijk maken om unit testen te schrijven. Deze leveren vooraf vastgelegde data af eerder dan de echte handelingen uit te voeren. Achteraf kunnen we ook controleren dat deze gebruikt zijn zoals verwacht. Dit past binnen het black box principe dat gehanteerd wordt voor unit testen. Jest bevat ingebouwde functionaliteit voor het maken van mocks.
Database
We hebben gekozen om onze database altijd in een aparte module te steken die onze collection exporteert. Dit maakt het makkelijk om deze te mocken. We gaan hierbij gebruik maken van de spyOn
functie van Jest om de functies van de database module te mocken.
De spyOn
functie maakt een mock van de find
functie van de collection
module. We geven aan dat deze mock de toArray
functie moet teruggeven met de waarde mockPets
. We controleren dan of de find
functie van de collection
module aangeroepen is met de juiste parameters.
Fetch
We gebruiken fetch om requests op externe services te doen. Omdat dit iets is dat je vaak wil mocken (om te vermijden dat netwerkstoringen testen doen falen, om te vermijden dat je API-limieten bereikt,...) is hier speciale ondersteuning voor.
We installeren eerst fetch-mock-jest (als development dependency).
De clientcode:
De testcode:
Neveneffecten vermijden
Om te vermijden dat andere operaties die fs.readFile nodig hebben niet fout lopen, moeten we zorgen dat de mock enkel in deze testfunctie gebruikt wordt. Daarom voegen we in de testfile deze regel toe:
Als we dit buiten de describe-blokken doen, gebeurt dit na elke test.
Last updated