Testen API's
Deze blog ben ik begonnen voornamelijk als een geheugensteun voor mijzelf. Afgelopen weken heb ik mijzelf er weer een paar keer op betrapt dat ik dacht hoe had ik dat ook weer bij een vorig project gedaan. Dat moet ik toch eens een keer gaan opschrijven. Komende tijd wil ik dan ook een aantal oplossingen gaan opschrijven van problemen waar ik iedere keer tegenaan loop.
De oplossingen die ik hier beschrijf zijn mogelijk niet de beste oplossingen, maar op het moment dat ik ze nodig had waren ze de beste die ik kon vinden of verzinnen. Met meer tijd of beter zoeken was er mogelijk een andere oplossing uit gekomen. Ik sta open voor betere/alternatieve oplossingen.
Testen van API's
Natuurlijk begin je eerst met unit testen, maar dat ga ik later nog wel beschrijven. Net zoals het integreren van de unit tests met Azure Devops en Docker.
Postman
Er zijn meerdere programma's/opties om een API te testen, zoals de standaard Swagger interface of SoapUI maar ik had het (gratis) postman tot mijn beschikking waarmee heel veel mogelijk is. Postman stelt je in staat om een collectie aan testen te maken die niet alleen gekoppeld zijn aan het aanroepen van een api methode, maar ook om de resultaten van een aanroep te gebruiken bij een volgende aanroep. Hierdoor is het mogelijk om bijvoorbeeld in de eerste aanroep een "order" aan te maken en in de volgende aanroep te controleren of de order ook daadwerkelijk in de database staat. De Post methode gevolgd door een Get.
Werkwijze
Iedere goede test(sessie) wil je betrouwbaar, snel en zo makkelijk mogelijk kunnen herhalen, zodra een nieuwe versie van een API is uitgerold wil je de regressie test binnen een paar minuten uitgevoerd kunnen hebben en het liefst door iedereen. Om dit te bewerkstelligen moet data van een vorige sessie niet in de weg zitten van de nieuwe sessie.
Aan het begin van de sessie zitten vaak een aantal calls voor het opschonen van data, of controleren dat alle systemen beschikbaar zijn. Of dat bepaalde data/queues/exchanges aangemaakt zijn. Iedere aanroep naar een API moet voorzien zijn van een test, op die manier is snel te zien waar iets fout is gegaan. Standaard controleer ik iedere call of er een status terug gekomen is die ik verwacht.
//Op de test tab van een API call om te controleren of de response een juiste statuscode heeft.
pm.test("Successful POST request", () => {
pm.expect(pm.response.code).to.be.oneOf([201,202,204]);
});
Variabelen
Postman gebruikt op verschillende niveaus variabelen, de niveaus hebben met name invloed op waar de variabelen hergebruikt kunnen worden.
- Local: Zijn alleen bruikbaar binnen het script, het request en de test van 1 aanroep.
- Environment: Zijn binnen alle aanroepen en tests bruikbaar, worden met name gebruikt voor het injecteren van omgevingsvariabelen zoals URLS e.d.
- Collection: Zijn herbruikbaar tussen verschillende aanroepen binnen een collectie. Zeer bruikbaar om gegevens tussen testen te hergebruiken.
- Global: Ook herbruikbaar tussen collections (heb ik nog niet gebruikt).
- Dynamische variabelen: Speciale variabelen van postman, zie hieronder.
Dynamische variabelen
Postman heeft hier een mooie oplossing voor met wat zij noemen dynamische variabelen, dit zijn waardes die iedere keer een nieuwe "random" waarde teruggeven maar wel dusdanig dat het niet helemaal random tekens zijn. Bijvoorbeeld $randomLastName
zal een fictieve achternaam teruggeven. Zo zijn er nog veel meer variabelen
Variabelen in de body
In de daadwerkelijke body van een request maakt het niet uit van welk niveau er een variabele gebruikt wordt. Alle variabelen hebben dezelfde notatie, zorg dus voor unieke namen!. Om een variabele te gebruiken wordt de naam van de variabele ingesloten in dubbele accolades {{naam_variabele}}
.
Variabelen kunnen gezet worden in zowel het Pre-request script als tijdens de test. Binnen de body kunnen ze alleen worden opgehaald.
Tips voor variabelen
Een dynamische variabele is niet alleen bruikbaar in de body, maar ook in het pre-request script. Zie hieronder voor een voorbeeld.
Indien een verzoek vaak herhaald wordt, maar met verschillende waardes vul ik de body met de namen van local variabelen welke ik dan in het Pre-Request script zet op basis van Collection variabelen. Op deze manier kan ik de body meermaals kopieren zonder dat daar aanpassingen nodig zijn en kan ik de aanpassingen alleen op het pre-request uitvoeren.
//Hier wordt de collection variabele col_description op een random productnaam gezet.
pm.collectionVariables.set("col_description", pm.variables.replaceIn('{{$randomProduct}}'));
//Vervolgens voor het gebruik in de body zet ik de local variabele body_description op de waarde van col_description.
pm.variables.set("body_description", pm.collectionVariables.get("col_description"));
In de body gebruik ik het dan als volgt:
{
"description": "{{body_description}}"
}
Testen
Na de aanroep volgen de testen, na de check op de juiste response code moet vaak de inhoud gecheckt worden. Om dit te kunnen doen moet de JSON data geparst worden. De eerste regels van de testen zien er vaak als volgt uit:
pm.test("Status code is 200", function () {
pm.response.to.have.status(200); //Check op een enkele statuscode
});
var jsonData = JSON.parse(pm.response.text()); //Parse de response naar JSON
Op dit moment is de jsonData beschikbaar en uit te vragen voor nadere tests. Het is ook mogelijk om functies te definieren in de tests om op die manier het testen gemakkelijker te maken.
Bijvoorbeeld om te controleren of een bepaalde text terugkomt in een array van items. Helaas is het (nog) niet mogelijk om functies te hergebruiken in meerdere scripts.
pm.test("Is de tekst Zoekterm terug gevonden in de array van artikelen", function () {
pm.expect(_isContains(jsonData.data.articles, "contents" ,"Zoekterm")).to.be.true;
});
function _isContains(json, keyname, value) {
return Object.keys(json).some(key => {
return typeof json[key] === 'object' ?
_isContains(json[key], keyname, value) : key === keyname && json[key] === value;
});
}
Herhalende aanroepen
Het is ook mogelijk om een aanroep meerdere malen te herhalen. Bijvoorbeeld voor het aanmaken van meerdere artikelen of orders o.i.d.
Hierbij is het van belang om een collection variabele te gebruiken om de count bij te houden (als er geen andere voorwaarde is waarop de loop kan stoppen.)
Door de setNextRequest
methode met de naam van een request aan te roepen. Kan het volgende verzoek bepaald worden.
var count = pm.collectionVariables.get("as_count");
if(count<20)
{
pm.collectionVariables.set("as_count", count+1);
setTimeout(function(){}, 1000);
postman.setNextRequest("PagingTest Repeater");
}
else
{
postman.setNextRequest(); //Zonder parameter dan gaat het verzoek verder met de volgende in de lijst
}
Docker
Zodra alle testen geschreven zijn en via collection run meermaals herhaald kunnen worden kan de collection geƫxporteerd worden samen met 1 of meer environment bestanden om gebruikt te worden in binnen een docker container welke als testrunner (door iedereen) gebruikt kan worden. Dit bestaat uit een docker-compose bestand waarin een newman (postman cli) commando uitgevoerd kan worden met zojuist geexporteerde collection en environment.
Dockerfile
FROM postman/newman
RUN npm install -g newman newman-reporter-htmlextra
WORKDIR /etc/newman
ENTRYPOINT ["newman"]
docker-compose
Onderstaande docker-compose bestand runt een postman collection met een environment bestand. De output van de tests worden gestuurd naar de console output en er wordt een HTML rapport gemaakt.
version: "2"
services:
postman_checks:
container_name: newman_test
build: .
image: postman_checks
command:
run postman_collection.json
-e 'test_environment.json'
-r htmlextra,cli
--reporter-htmlextra-export reports/test_report.html
volumes:
- ./:/etc/newman
Middels een docker-compose up --build
wordt de test uitgevoerd en worden de resultaten gepresenteerd.