Introductie
Het schrijven van unit-tests is iets wat zowel beginners als doorgewinterde ingenieurs doorgaans uitstellen tot latere ontwikkelingsfasen, maar toch zijn ze de sleutel tot stabiele en robuuste softwareontwikkeling.
Het uitgangspunt van testgestuurde ontwikkeling (TDD) is het schrijven van uw tests, zelfs voordat u begint met coderen. Dat is een geweldig doel om naar te streven, maar het vergt veel discipline en planning als je de principes ervan probeert te volgen! Om dit hele proces een stuk eenvoudiger te maken, kun je gebruik maken van eenvoudig te gebruiken en krachtige test- en beweringsframeworks, zoals Mocha en Chai.
In dit artikel laten we u eerst kennismaken met deze twee bibliotheken en laten we u vervolgens zien hoe u ze samen kunt gebruiken om snel leesbare en functionele unit-tests te maken.
Chai
Chai is een beweringsbibliotheek die zowel de BDD (gedragsgestuurde ontwikkeling) en TDD (testgestuurde ontwikkeling) programmeerstijlen voor het testen van code, en is bedoeld om te worden gecombineerd met een testbibliotheek waarmee u tests kunt organiseren. Het wordt heel vaak gecombineerd met mokka.
Het heeft drie hoofd-API's:
should
expect
assert
var.should.equal(var2)
expect.var.to.be.equal(var2)
assert.equal(var1, var2)
In dit artikel zullen we ons concentreren op de BDD-stijl met behulp van Chai's
expect
interface, hoewel het gebruik van andere interfaces/stijlen volgens uw eigen intuรฏtie prima is. Deassert
interface is het meest voorkomende TDD-beweringframework.
expect
gebruikt een zeer natuurlijke taal-API om uw beweringen te schrijven, waardoor uw tests later gemakkelijker te schrijven en te verbeteren zijn. Dit wordt gedaan door getters aan elkaar te koppelen om de bewering te creรซren en uit te voeren, waardoor het gemakkelijker wordt om vereisten in code te vertalen:
let user = {name: 'Scott'};
expect(user).to.have.property('name');
Opmerking: Zie je hoe je de bewering vrijwel in natuurlijke taal kunt lezen en kunt begrijpen wat er aan de hand is? Dat is een van de belangrijkste voordelen van het gebruik van een beweringsbibliotheek zoals Chai!
Nog een paar voorbeelden van deze getters zijn:
to
be
is
and
has
have
Een flink aantal van deze getters kunnen aan elkaar worden gekoppeld en worden gebruikt met beweringsmethoden zoals true
, ok
, exist
en empty
om enkele complexe beweringen in slechts รฉรฉn regel te creรซren:
"use strict";
const expect = require('chai').expect;
expect({}).to.exist;
expect(26).to.equal(26);
expect(false).to.be.false;
expect('hello').to.be.string;
expect([1, 2, 3]).to.not.be.empty;
expect([1, 2, 3]).to.have.length.of.at.least(3);
Opmerking: Een volledige lijst van de beschikbare methoden kunt u vinden hier.
Misschien wilt u ook de lijst met beschikbare opties bekijken plugins voor Chai. Deze maken het veel eenvoudiger om complexere functies te testen.
Nemen chai-http Dit is bijvoorbeeld een plug-in waarmee u serverroutes kunt testen:
"use strict";
const chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
chai.request(app)
.put('/api/auth')
.send({username: '[email protected]', password: 'abc123'})
.end(function(err, res) {
expect(err).to.be.null;
expect(res).to.have.status(200);
});
Testcases organiseren met Mokka โ beschrijven() en it()
Mocha is een testframework voor Node dat u de flexibiliteit geeft om asynchrone (of synchrone) code serieel uit te voeren. Alle niet-afgevangen uitzonderingen worden weergegeven naast de testcase waarin deze is gegooid, waardoor het gemakkelijk wordt om precies te identificeren wat er is mislukt en waarom.
Het wordt geadviseerd om Mocha wereldwijd te installeren:
$ npm install mocha -g
Je wilt dat het een globale installatie is sinds de mocha
opdracht wordt gebruikt om de tests voor het project in uw lokale map uit te voeren.
Wat doet dit stukje code?
it()
moet X teruggeven. it()
definieert testgevallen, en Mocha zal ze allemaal uitvoeren it()
als unittest. We kunnen meerdere unit-tests organiseren describe()
een gemeenschappelijke functionaliteit, en zo Mocha-tests structureren.
Dit kan waarschijnlijk het beste worden beschreven met een concreet voorbeeld:
"use strict";
const expect = require('chai').expect;
describe('Math', function() {
describe('#abs()', function() {
it('should return positive value of given negative number', function() {
expect(Math.abs(-5)).to.be.equal(5);
});
});
});
In het describe()
methode hebben we a gedefinieerd test naam, Genaamd #abs()
. U kunt ook afzonderlijk tests uitvoeren op basis van hun naam. Dit wordt later besproken.
Opmerking: Met Mokka-testen is dat niet nodig require()
een van de Mocha-methoden. Deze methoden worden globaal aangeboden wanneer ze worden uitgevoerd met de mocha
opdracht.
Om deze tests uit te voeren, slaat u uw bestand op en gebruikt u de mocha
opdracht:
$ mocha .
Math
#abs()
โ should return positive value of given number
1 passing (9ms)
De output is een overzicht van de uitgevoerde tests en hun resultaten. Merk op hoe het genest is describe()
oproepen worden overgedragen naar de resultaatuitvoer. Het is handig om alle tests voor een bepaalde methode of functie samen te nesten.
Deze methoden vormen de basis voor het Mocha-testraamwerk. Gebruik ze om uw toetsen samen te stellen en te organiseren zoals u dat wilt. We zullen hiervan een voorbeeld zien in de volgende sectie.
Testen schrijven met mokka en chai
De aanbevolen manier om uw tests binnen uw project te organiseren, is door ze allemaal in hun eigen project te plaatsen /test
map. Standaard controleert Mocha op unit-tests met behulp van de klodders ./test/*.js
en ./test/*.coffee
. Van daaruit zal het elk bestand laden en uitvoeren dat het describe()
methode.
Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!
Het is gebruikelijk om de testbestanden te voorzien van het achtervoegsel .test.js
voor de bronbestanden die Mocha-tests bevatten:
โโโ package.json
โโโ lib
โ โโโ db.js
โ โโโ models.js
โ โโโ util.js
โโโ test
โโโ db.test.js
โโโ models.test.js
โโโ util.test.js
โโโ util.js
util.js
in de test
directory zou geen unit-tests bevatten, alleen hulpprogramma-functies om te helpen bij het testen.
Note: U kunt elke structuur gebruiken die voor u zinvol is, de unit-tests worden automatisch opgepakt.
Als het gaat om het daadwerkelijk schrijven van de tests, helpt het om ze te organiseren met behulp van de describe()
methoden. U kunt ze ordenen op kenmerk, functie, bestand of elk ander willekeurig niveau. Een testbestand dat is georganiseerd om de werking op functieniveau te beschrijven, ziet er bijvoorbeeld als volgt uit:
"use strict";
const expect = require('chai').expect;
describe('Math', function() {
describe('#abs()', function() {
it('should return positive value of given negative number', function() {
expect(Math.abs(-5)).to.be.equal(5);
});
it('should return positive value of given positive number', function() {
expect(Math.abs(3)).to.be.equal(3);
});
it('should return 0 given 0', function() {
expect(Math.abs(0)).to.be.equal(0);
});
});
});
Als u de tests uitvoert, krijgt u dan de uitvoer:
$ mocha .
Math
#abs()
โ should return positive value of given negative number
โ should return positive value of given positive number
โ should return 0 given 0
3 passing (11ms)
Als je het nog verder uitbreidt, kun je zelfs tests voor meerdere methoden in รฉรฉn bestand hebben. In dit geval worden de methoden gegroepeerd op basis van de Math
voorwerp:
"use strict";
const expect = require('chai').expect;
describe('Math', function() {
describe('#abs()', function() {
it('should return positive value of given negative number', function() {
expect(Math.abs(-5)).to.be.equal(5);
});
it('should return positive value of given positive number', function() {
expect(Math.abs(3)).to.be.equal(3);
});
it('should return 0 given 0', function() {
expect(Math.abs(0)).to.be.equal(0);
});
});
describe('#sqrt()', function() {
it('should return the square root of a given positive number', function() {
expect(Math.sqrt(25)).to.be.equal(5);
});
it('should return NaN for a given negative number', function() {
expect(Math.sqrt(-9)).to.be.NaN;
});
it('should return 0 given 0', function() {
expect(Math.sqrt(0)).to.be.equal(0);
});
});
});
Wat resulteert in:
$ mocha .
Math
#abs()
โ should return positive value of given negative number
โ should return positive value of given positive number
โ should return 0 given 0
#sqrt()
โ should return the square root of a given positive number
โ should return NaN for a given negative number
โ should return 0 given 0
6 passing (10ms)
Mokkahaken โ before(), after(), beforeEach() en afterEach()
Toegegeven, de meeste unit-tests zijn niet zo eenvoudig. Vaak heb je waarschijnlijk andere bronnen nodig om je tests uit te voeren, zoals een database of een andere externe bron (of een namaak/stub daarvan). Om dit in te stellen, kunnen we een of meer van de volgende gebruiken Mokka haak methoden:
before()
: Wordt uitgevoerd vรณรณr alle tests in het gegeven blokbeforeEach()
: Wordt uitgevoerd vรณรณr elke test in het gegeven blokafter()
: Wordt uitgevoerd na alle tests in het opgegeven blokafterEach()
: Wordt uitgevoerd na elke test in het gegeven blok
Deze haken zijn de perfecte plek voor het uitvoeren van op- en afbouwwerkzaamheden die nodig zijn voor uw tests. Een van de meest voorkomende gebruiksscenario's is het tot stand brengen van een verbinding met uw database voordat u de tests uitvoert:
"use strict";
const expect = require('chai').expect;
let User = require('../models').User;
describe('Users', function() {
let database = null;
before(function(done) {
});
afterEach(function(done) {
});
describe('#save()', function() {
it('should save User data to database', function(done) {
});
});
describe('#load()', function() {
it('should load User data from database', function(done) {
});
});
});
Voor elke van de tests wordt uitgevoerd, wordt de functie naar onze verzonden before()
methode wordt uitgevoerd (en slechts รฉรฉn keer tijdens de tests), waardoor een verbinding met de database tot stand wordt gebracht. Zodra dit is gebeurd, worden onze testsuites uitgevoerd.
Omdat we niet willen dat de gegevens van de ene testsuite onze andere tests beรฏnvloeden, moeten we de gegevens uit onze database wissen nadat elke suite is uitgevoerd. Dit is wat afterEach()
is voor. We gebruiken deze hook om daarna alle databasegegevens te wissen elk testcase is uitgevoerd, zodat we met een schone lei kunnen beginnen voor de volgende tests.
Mokka-testen uitvoeren
In de meeste gevallen is dit onderdeel vrij eenvoudig. Ervan uitgaande dat je Mocha al hebt geรฏnstalleerd en naar de projectmap bent genavigeerd, hoeven de meeste projecten alleen de mocha
commando zonder argumenten om hun tests uit te voeren:
$ mocha
Math
#abs()
โ should return positive value of given negative number
โ should return positive value of given positive number
โ should return 0 given 0
#sqrt()
โ should return the square root of a given positive number
โ should return NaN for a given negative number
โ should return 0 given 0
6 passing (10ms)
Dit is iets anders dan onze vorige voorbeelden, omdat we Mocha niet hoefden te vertellen waar onze tests zich bevonden. In dit voorbeeld bevindt de testcode zich op de verwachte locatie van /test
.
Er zijn echter enkele handige opties die u mogelijk wilt gebruiken bij het uitvoeren van tests. Als sommige van uw tests bijvoorbeeld mislukken, wilt u waarschijnlijk niet de hele suite uitvoeren telkens wanneer u een wijziging aanbrengt. Bij sommige projecten kan het voltooien van de volledige testsuite enkele minuten duren. Dat is een hoop tijdverspilling als je eigenlijk maar รฉรฉn test hoeft uit te voeren.
Voor dit soort gevallen zou je dat wel moeten doen vertel Mocha welke tests je moet uitvoeren. Dit kan met behulp van de
-g
or-f
opties.
Om individuele tests uit te voeren, kunt u de -g
vlag en voeg een gemeenschappelijk patroon toe tussen de tests die u wilt uitvoeren. Als u bijvoorbeeld de #sqrt()
testen:
$ mocha -g sqrt
Math
#sqrt()
โ should return the square root of a given positive number
โ should return NaN for a given negative number
โ should return 0 given 0
3 passing (10ms)
Merk op dat de #abs()
tests waren niet opgenomen in deze run. Als u dienovereenkomstig plant met uw testnamen, kan deze optie worden gebruikt om alleen specifieke delen van uw tests uit te voeren.
Dit zijn echter niet de enige nuttige opties. Hier zijn nog een paar opties voor mokka die je misschien wilt bekijken:
--invert
: Inverteert-g
en-f
lucifers--recursive
: Inclusief submappen--harmony
: schakel alle harmoniefuncties in Node in
Note: U kunt de volledige lijst met opties bekijken met behulp van de mocha -h
commando, of aan deze pagina.
Conclusie
Houd er rekening mee dat zowel Mocha als Chai kunnen worden gebruikt voor het testen van vrijwel elk type Node-project, of het nu een bibliotheek, een opdrachtregelprogramma of zelfs een website is. Door gebruik te maken van de verschillende opties en plug-ins die voor u beschikbaar zijn, zou u vrij gemakkelijk aan uw testbehoeften moeten kunnen voldoen. Elk van deze bibliotheken is erg handig voor het valideren van uw code en zou in vrijwel al uw Node-projecten moeten worden gebruikt.
Hopelijk heeft dit als een nuttige introductie tot Mokka en Chai gediend. Er valt nog veel meer te leren dan wat ik hier heb gepresenteerd, dus bekijk zeker de documentatie voor meer informatie.