Testowanie kodu Node.js z Mocha i Chai

Wprowadzenie

Pisanie testów jednostkowych jest czymś, co zarówno początkujący, jak i doświadczeni inżynierowie zwykle odkładają na późniejszą fazę rozwoju, a jednak są one kluczem do stabilnego i solidnego tworzenia oprogramowania.

Podstawowe założenie programowanie sterowane testami (TDD) to pisanie testów jeszcze przed rozpoczęciem kodowania. To wspaniały cel, do którego należy dążyć, ale przestrzeganie jego zasad wymaga dużo dyscypliny i planowania! Aby znacznie uprościć ten proces, możesz skorzystać z łatwych w użyciu i wydajnych frameworków do testowania i asercji, takich jak Mokka i Chai.

W tym artykule zaczniemy od zapoznania się z tymi dwiema bibliotekami, a następnie pokażemy, jak używać ich razem, aby szybko tworzyć czytelne i funkcjonalne testy jednostkowe.

Chai

Chai to biblioteka asercji, która zapewnia zarówno BDD (rozwój oparty na zachowaniu) i TDD (programowanie sterowane testami) stylów programowania do testowania kodu i ma być sparowany z biblioteką testową, która pozwala organizować testy. Jest bardzo często łączony z Mocha.

Ma trzy główne interfejsy API:

  • should
  • expect
  • assert

var.should.equal(var2)


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


assert.equal(var1, var2)

W tym artykule skupimy się na stylu BDD przy użyciu Chai expect interface, chociaż używanie innych interfejsów/stylów zgodnie z własną intuicją jest całkowicie w porządku. The assert interface to najbardziej podobne wspólne frameworki asercji TDD.

expect używa API w bardzo naturalnym języku do pisania twierdzeń, co ułatwi pisanie i ulepszanie testów w późniejszym czasie. Odbywa się to poprzez łączenie getterów w celu utworzenia i wykonania asercji, co ułatwia przełożenie wymagań na kod:

let user = {name: 'Scott'};


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

Uwaga: Zobacz, jak możesz w dużym stopniu przeczytać twierdzenie w języku naturalnym i zrozumieć, co się dzieje? To jedna z głównych zalet korzystania z biblioteki asercji, takiej jak Chai!

Kilka innych przykładów tych getterów to:

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

Sporo z tych getterów można połączyć razem i używać z metodami asercji, takimi jak true, ok, exist, empty aby utworzyć kilka złożonych twierdzeń w jednym wierszu:

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

Uwaga: Pełną listę dostępnych metod można znaleźć tutaj.

Możesz także sprawdzić listę dostępnych wtyczki dla Chaia. Ułatwiają one testowanie bardziej złożonych funkcji.

Brać chai-http na przykład, która jest wtyczką, która pomaga testować trasy serwera:

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

Organizowanie przypadków testowych za pomocą Mokki – opisz() i it()

Mocha to platforma testowa dla Node, która zapewnia elastyczność szeregowego uruchamiania kodu asynchronicznego (lub synchronicznego). Wszelkie nieprzechwycone wyjątki są wyświetlane obok przypadku testowego, w którym został rzucony, co ułatwia dokładne określenie, co zawiodło i dlaczego.

Zaleca się globalną instalację Mocha:

$ npm install mocha -g

Będziesz chciał, aby była to instalacja globalna, ponieważ mocha Polecenie służy do uruchamiania testów dla projektu w katalogu lokalnym.

Co robi ten fragment kodu?

it() powinien zwrócić X. it() definiuje przypadki testowe, a Mocha uruchamia każdy z nich it() jako test jednostkowy. Możemy zorganizować wiele testów jednostkowych describe() wspólną funkcjonalność, a co za tym idzie strukturę testów Mocha.

Prawdopodobnie najlepiej będzie to opisać na konkretnym przykładzie:

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

W describe() metoda, zdefiniowaliśmy a Nazwa testu, Zwane #abs(). Możesz również indywidualnie uruchamiać testy według ich nazw — zostanie to omówione później.

Uwaga: Z testami Mocha nie musisz require() którąkolwiek z metod Mocha. Te metody są udostępniane globalnie, gdy są uruchamiane z mocha dowództwo.

Aby uruchomić te testy, zapisz plik i użyj rozszerzenia mocha polecenie:

$ mocha .

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


  1 passing (9ms)

Dane wyjściowe to zestawienie przeprowadzonych testów i ich wyników. Zwróć uwagę, jak zagnieżdżone describe() wywołania są przenoszone do wyjścia wyników. Przydatne jest zagnieżdżenie wszystkich testów dla danej metody lub funkcji.

Metody te stanowią podstawę ram testowych Mocha. Używaj ich do komponowania i organizowania testów w dowolny sposób. W następnej sekcji zobaczymy jeden z przykładów.

Pisanie testów z Mocha i Chai

Zalecanym sposobem zorganizowania testów w ramach projektu jest umieszczenie ich wszystkich w osobnych /test informator. Domyślnie Mocha sprawdza testy jednostkowe przy użyciu globów ./test/*.js i ./test/*.coffee. Stamtąd załaduje i wykona każdy plik, który wywołuje metodę describe() Metoda.

Zapoznaj się z naszym praktycznym, praktycznym przewodnikiem dotyczącym nauki Git, zawierającym najlepsze praktyki, standardy przyjęte w branży i dołączoną ściągawkę. Zatrzymaj polecenia Google Git, a właściwie uczyć się to!

Powszechne jest dodawanie sufiksów do plików testowych .test.js dla plików źródłowych zawierających testy Mocha:

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

util.js test katalog nie zawierałby żadnych testów jednostkowych, tylko funkcje narzędziowe pomagające w testowaniu.

Note: Możesz użyć dowolnej struktury, która ma dla ciebie sens, testy jednostkowe są pobierane automatycznie.

Jeśli chodzi o faktyczne pisanie testów, pomaga je zorganizować za pomocą describe() metody. Możesz je uporządkować według funkcji, funkcji, pliku lub dowolnego innego dowolnego poziomu. Na przykład plik testowy zorganizowany w celu opisania działania na poziomie funkcji wygląda następująco:

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

Uruchomienie testów dałoby wtedy wynik:

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

Rozwijając się jeszcze bardziej, możesz nawet mieć testy dla wielu metod w jednym pliku. W tym przypadku metody są pogrupowane według Math obiekt:

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

Co skutkuje w:

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

Mokka Hooks – before(), after(), beforeEach() i afterEach()

Trzeba przyznać, że większość testów jednostkowych nie jest taka prosta. Wiele razy prawdopodobnie będziesz potrzebować innych zasobów do przeprowadzenia testów, takich jak baza danych lub inny zasób zewnętrzny (lub ich makieta/odcinek). Aby to skonfigurować, możemy użyć jednego lub więcej z poniższych Hak mokka metody:

  • before(): Uruchamia przed wszystkimi testami w danym bloku
  • beforeEach(): Uruchamia przed każdym testem w danym bloku
  • after(): Uruchamia się po wszystkich testach w danym bloku
  • afterEach(): Uruchamia się po każdym teście w danym bloku

Te haczyki są idealnym miejscem do wykonywania prac związanych z konfiguracją i demontażem wymaganych do testów. Jednym z typowych przypadków użycia jest ustanowienie połączenia z bazą danych przed uruchomieniem testów:

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

Przed każdy testów są uruchamiane, funkcja wysyłana do naszego before() metoda jest uruchamiana (i uruchamiana tylko raz podczas testów), która ustanawia połączenie z bazą danych. Po wykonaniu tej czynności uruchamiane są nasze zestawy testów.

Ponieważ nie chcielibyśmy, aby dane z jednego zestawu testów wpływały na nasze inne testy, musimy wyczyścić dane z naszej bazy danych po uruchomieniu każdego zestawu. Co to jest afterEach() jest dla. Używamy tego haka, aby później wyczyścić wszystkie dane bazy danych każdy przypadek testowy jest uruchamiany, więc możemy zacząć z czystym kontem do kolejnych testów.

Uruchamianie testów Mocha

W większości przypadków ta część jest dość prosta. Zakładając, że już zainstalowałeś Mocha i przeszedłeś do katalogu projektu, większość projektów wystarczy użyć mocha polecenie bez argumentów, aby uruchomić swoje testy:

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

Jest to nieco inne niż nasze poprzednie przykłady, ponieważ nie musieliśmy mówić firmie Mocha, gdzie znajdowały się nasze testy. W tym przykładzie kod testowy znajduje się w oczekiwanej lokalizacji /test.

Istnieje jednak kilka przydatnych opcji, których możesz chcieć użyć podczas uruchamiania testów. Na przykład, jeśli niektóre testy kończą się niepowodzeniem, prawdopodobnie nie chcesz uruchamiać całego pakietu za każdym razem, gdy wprowadzasz zmianę. W przypadku niektórych projektów wykonanie pełnego zestawu testów może zająć kilka minut. To dużo straconego czasu, jeśli naprawdę potrzebujesz tylko jednego testu.

W takich przypadkach powinieneś powiedz Mocha, które testy ma przeprowadzić. Można to zrobić za pomocą -g or -f opcje.

Aby uruchomić poszczególne testy, możesz podać plik -g flag i dodaj wspólny wzorzec między testami, które chcesz uruchomić. Na przykład, jeśli chcesz uruchomić plik #sqrt() testy:

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

Zauważ, że #abs() testy nie zostały uwzględnione w tym przebiegu. Jeśli odpowiednio zaplanujesz nazwy testów, tej opcji można użyć tylko do uruchamiania określonych sekcji testów.

To jednak nie jedyne przydatne opcje. Oto kilka innych opcji Mocha, które możesz chcieć sprawdzić:

  • --invert: Odwraca -g i -f zapałki
  • --recursive: Dołącz podkatalogi
  • --harmony: Włącz wszystkie funkcje harmonii w Node

Note: Pełną listę opcji można sprawdzić za pomocą mocha -h polecenie lub wł tutaj.

Gdzie dowiedzieć się więcej? W tym temacie jest o wiele więcej, niż możemy omówić w krótkim artykule, więc jeśli chcesz dowiedzieć się więcej, zalecamy sprawdzenie oficjalnego Mokka i Chai dokumentacja.

Wnioski

Pamiętaj, że zarówno Mocha, jak i Chai mogą być używane do testowania niemal każdego typu projektu Node, niezależnie od tego, czy jest to biblioteka, narzędzie wiersza poleceń, czy nawet strona internetowa. Korzystając z różnych dostępnych opcji i wtyczek, powinieneś być w stanie dość łatwo zaspokoić swoje potrzeby testowe. Każda z tych bibliotek jest bardzo przydatna do sprawdzania poprawności kodu i powinna być używana w prawie wszystkich projektach Node.

Mamy nadzieję, że posłużyło to jako przydatne wprowadzenie do Mocha i Chai. Jest o wiele więcej do nauczenia się niż to, co tutaj przedstawiłem, więc koniecznie sprawdź dokumenty, aby uzyskać więcej informacji.

Znak czasu:

Więcej z Nadużycie stosu