Testar Node.js-koden med Mocha och Chai

Beskrivning

Att skriva enhetstester är något både nybörjare och erfarna ingenjörer brukar skjuta upp för senare utvecklingsfaser, men ändå – de är nyckeln till stabil och robust mjukvaruutveckling.

Den grundläggande förutsättningen för testdriven utveckling (TDD) skriver dina tester redan innan du börjar koda. Det är ett bra mål att sträva efter, men det krävs mycket disciplin och planering när du försöker följa dess principer! För att göra hela denna process mycket enklare kan du ta till lättanvända och kraftfulla test- och påståenderamverk, som t.ex. Mocka och Chai.

I den här artikeln kommer vi att börja med att introducera dig till dessa två bibliotek och sedan visa dig hur du använder dem tillsammans för att snabbt skapa läsbara och funktionella enhetstester.

Chai

Chai är ett påståendebibliotek som tillhandahåller både BDD (beteendedriven utveckling) och TDD (testdriven utveckling) programmeringsstilar för att testa kod, och är tänkt att paras ihop med ett testbibliotek som låter dig organisera tester. Det är mycket vanligt att paras ihop med Mocha.

Den har tre huvudsakliga API:er:

  • should
  • expect
  • assert

var.should.equal(var2)


expect.var.to.be.equal(var2)


assert.equal(var1, var2)

I den här artikeln kommer vi att fokusera på BDD-stilen med Chai's expect gränssnitt, men att använda andra gränssnitt/stilar enligt din egen intuition är helt okej. De assert gränssnittet är de mest lika vanliga TDD-ramverken.

expect använder ett mycket naturligt språk API för att skriva dina påståenden, vilket kommer att göra dina tester lättare att skriva och förbättra senare på vägen. Detta görs genom att kedja samman getters för att skapa och exekvera påståendet, vilket gör det lättare att översätta krav till kod:

let user = {name: 'Scott'};


expect(user).to.have.property('name');

Notera: Se hur du i stort sett kan läsa påståendet på ett naturligt språk och förstå vad som händer? Det är en av de största fördelarna med att använda ett påståendebibliotek som Chai!

Några fler exempel på dessa getters är:

  • to
  • be
  • is
  • and
  • has
  • have

En hel del av dessa getters kan kedjas ihop och användas med påståendemetoder som true, ok, existoch empty för att skapa några komplexa påståenden på bara en rad:

"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);

Notera: En fullständig lista över tillgängliga metoder finns här..

Du kanske också vill kolla in listan över tillgängliga insticksmoduler för Chai. Dessa gör det mycket lättare att testa mer komplexa funktioner.

Ta chai-http till exempel, som är ett plugin som hjälper dig att testa servervägar:

"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);
    });

Organisera testfall med Mocha – describe() och it()

Mocha är ett testramverk för Node som ger dig flexibiliteten att köra asynkron (eller synkron) kod seriellt. Eventuella oupptäckta undantag visas bredvid testfallet där det kastades, vilket gör det enkelt att identifiera exakt vad som misslyckades och varför.

Det rekommenderas att installera Mocha globalt:

$ npm install mocha -g

Du vill att det ska vara en global installation sedan mocha kommandot används för att köra testen för projektet i din lokala katalog.

Vad gör den här kodbiten?

it() ska returnera X. it() definierar testfall, och Mocha kommer att köra vart och ett it() som ett enhetstest. För att organisera flera enhetstester kan vi describe() en gemensam funktionalitet, och därmed strukturera Mocha-tester.

Detta beskrivs förmodligen bäst med ett konkret exempel:

"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);
        });
    });
});

I describe() metod definierade vi en testnamn, Som kallas #abs(). Du kan också köra tester individuellt efter deras namn – detta kommer att behandlas senare.

Notera: Med Mocha-tester behöver du inte göra det require() någon av mockametoderna. Dessa metoder tillhandahålls globalt när de körs med mocha kommando.

För att köra dessa tester, spara din fil och använd mocha kommando:

$ mocha .

  Math
    #abs()
      ✓ should return positive value of given number 


  1 passing (9ms)

Resultatet är en uppdelning av de tester som kördes och deras resultat. Lägg märke till hur kapslingen describe() anrop överförs till resultatet. Det är användbart att ha alla tester för en given metod eller funktion kapslade ihop.

Dessa metoder är grunden för Mocha-testramverket. Använd dem för att komponera och organisera dina tester som du vill. Vi kommer att se ett exempel på detta i nästa avsnitt.

Att skriva prov med Mocka och Chai

Det rekommenderade sättet att organisera dina tester inom ditt projekt är att lägga alla i sina egna /test katalog. Som standard söker Mocha efter enhetstester med hjälp av globs ./test/*.js och ./test/*.coffee. Därifrån kommer den att ladda och köra alla filer som anropar describe() metod.

Kolla in vår praktiska, praktiska guide för att lära dig Git, med bästa praxis, branschaccepterade standarder och medföljande fuskblad. Sluta googla Git-kommandon och faktiskt lära Det!

Det är vanligt att suffixa testfilerna med .test.js för källfilerna som innehåller Mocha-tester:

├── package.json
├── lib
│   ├── db.js
│   ├── models.js
│   └── util.js
└── test
    ├── db.test.js
    ├── models.test.js
    ├── util.test.js
    └── util.js

util.js i test katalogen skulle inte innehålla några enhetstester, bara verktygsfunktioner för att hjälpa till med testning.

Anmärkningar: Du kan använda vilken struktur som helst som är meningsfull för dig, enhetstesten plockas upp automatiskt.

När det gäller att faktiskt skriva proven, hjälper det att organisera dem med hjälp av describe() metoder. Du kan organisera dem efter funktion, funktion, fil eller någon annan godtycklig nivå. Till exempel, en testfil som är organiserad för att beskriva arbetet på funktionsnivå ser ut så här:

"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);
        });
    });
});

Om du kör testerna får du resultatet:

$ 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)

Om du expanderar ytterligare kan du till och med ha tester för flera metoder i en enda fil. I det här fallet är metoderna grupperade efter Math objekt:

"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);
        });
    });
});

Vilket resulterar i:

$ 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)

Mocha Hooks – before(), after(), beforeEach() och afterEach()

Visserligen är de flesta enhetstester inte så enkla. Många gånger kommer du förmodligen att behöva andra resurser för att utföra dina tester, som en databas eller någon annan extern resurs (eller en låtsas av dem). För att ställa in detta kan vi använda ett eller flera av följande Mockakrok metoder:

  • before(): Körs före alla tester i det givna blocket
  • beforeEach(): Körs före varje test i det givna blocket
  • after(): Körs efter alla tester i det givna blocket
  • afterEach(): Körs efter varje test i det givna blocket

Dessa krokar är den perfekta platsen för att utföra installations- och rivningsarbeten som krävs för dina tester. Ett av de vanligaste användningsfallen är att upprätta en anslutning till din databas innan du kör testerna:

"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) {
            
        });
    });
});

Innan vilken som helst av testerna körs skickas funktionen till vår before() metod körs (och körs endast en gång under testerna), vilket upprättar en anslutning till databasen. När detta är gjort körs sedan våra testsviter.

Eftersom vi inte vill att data från en testsvit ska påverka våra andra tester, måste vi rensa data från vår databas efter att varje svit har körts. Det här är vad afterEach() är för. Vi använder denna krok för att rensa all databasdata efteråt varje testfall körs, så vi kan börja från ett rent blad för nästa test.

Kör Mockatest

För de flesta fall är denna del ganska enkel. Förutsatt att du redan har installerat Mocha och navigerat till projektkatalogen, behöver de flesta projekt bara använda mocha kommando utan argument för att köra sina tester:

$ 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)

Detta är något annorlunda än våra tidigare exempel eftersom vi inte behövde berätta för Mocha var våra tester fanns. I det här exemplet är testkoden på den förväntade platsen för /test.

Det finns dock några användbara alternativ som du kanske vill använda när du kör tester. Om några av dina test misslyckas, till exempel, vill du förmodligen inte köra hela sviten varje gång du gör en ändring. För vissa projekt kan hela testsviten ta några minuter att slutföra. Det är mycket bortkastad tid om du verkligen bara behöver köra ett test.

För fall som detta bör du berätta för Mocha vilka tester som ska köras. Detta kan göras med hjälp av -g or -f alternativ.

För att köra individuella tester kan du tillhandahålla -g flagga och lägg till ett gemensamt mönster mellan de tester du vill köra. Till exempel, om du vill köra #sqrt() tester:

$ 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)

Observera att #abs() tester ingick inte i denna körning. Om du planerar i enlighet med dina testnamn kan det här alternativet användas för att endast köra specifika delar av dina tester.

Dessa är dock inte de enda användbara alternativen. Här är några fler alternativ för Mocha som du kanske vill kolla in:

  • --invert: Inverterar -g och -f tändstickor
  • --recursive: Inkludera underkataloger
  • --harmony: Aktivera alla harmonifunktioner i Node

Anmärkningar: Du kan kolla in hela listan med alternativ genom att använda mocha -h kommando eller på denna sida.

Var kan man lära sig mer? Det finns mycket mer i detta ämne än vi kan täcka i en kort artikel, så om du vill lära dig mer rekommenderar vi att du kollar in den officiella Mocka och Chai dokumentation.

Slutsats

Tänk på att både Mocha och Chai kan användas för att testa nästan alla typer av nodprojekt, oavsett om det är ett bibliotek, kommandoradsverktyg eller till och med en webbplats. Genom att använda de olika alternativen och plugins som är tillgängliga för dig bör du kunna tillfredsställa dina testbehov ganska enkelt. Vart och ett av dessa bibliotek är mycket användbart för att validera din kod och bör användas i nästan alla dina Node-projekt.

Förhoppningsvis har detta fungerat som en användbar introduktion till Mocha och Chai. Det finns mycket mer att lära än vad jag har presenterat här, så se till att kolla in dokumenten för mer information.

Tidsstämpel:

Mer från Stackabuse