[JS] Testowanie kodu JavaScript za pomocą Gulp.js, Karma i Mocha.js
W dzisiejszych czasach testowanie kodu JavaScript jest niezbędne. Dzięki przetestowanemu kodowi jesteśmy zabezpieczeni, że jakiekolwiek przyszłe zmiany w kodzie i problemy z nimi związane, zostaną szybko wykryte i zmuszą programistą do przemyślenia swojego kodu i aktualizacji scenariuszy testowych.
Scenariusze testów stanowią swoistego rodzaju poradnik dotyczący tego jak działa kod, jak używać dany kawałek kodu oraz czego się można po nim spodziewać. Testując nasz kod, możemy wykryć kod, który jest zbędny, a który został dodany na jakimś etapie rozwoju oprogramowania, bo mieliśmy jakąś koncepcję na rozwiązanie problemu, która ostatecznie nie została wdrożona.
Mając przygotowane testy jednostkowe JavaScript, warto się pokusić o przygotowanie informacji dotyczącej tego ile naszego kodu zostało pokryte testami, tzw. code coverage. Dzięki temu, mamy podpowiedź dotyczącą kodu, który mógł jeszcze nie zostać przetestowany.
Warto przypomnieć, że nasze testy jednostkowe będą uruchamiane na node.js. Dlatego najpierw musisz go zainstalować na swoim komputerze.
Przykładowy kod pluginu JavaScript
Nasz przykładowy kod, będzie bardzo prostym pluginem, który będzie pozwalał na jakiś stopień konfiguracji przez innych programistów korzystających z naszego rozwiązania. Nasz kod będzie pełnił rolę pluginu, który będzie pozwalał nam zdefiniować, które elementy mają być klikalne. W przypadku naszego pluginu, po kliknięciu w jakiś element będzie pojawiał się tekst w kontenerze logów (i ewentualnie animacja pojawienia się tekstu).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | // clicker.js (function (window, document) { 'use strict'; window.Clicker = function (customParams) { var CLASS_LOGS = 'logs', CLASS_LOGS_ANIMATABLE = CLASS_LOGS + '--animatable', CLASS_CLICKER = 'clicker', CLASS_CLICKER_READY = CLASS_CLICKER + '--ready', SELECTOR_CLICKER = '.' + CLASS_CLICKER, logsContainer = document.createElement('div'), params = { selector: SELECTOR_CLICKER, isAnimatable: false }, transitions = { transition: 'transitionend', OTransition: 'oTransitionEnd', MozTransition: 'transitionend', WebkitTransition: 'webkitTransitionEnd' }, paramKey, elements, detectedTransitionEvent, transitionKey, clickCallback = function (event) { var text = document.createElement('p'), triggeredOpacity; text.innerHTML = 'Click!'; logsContainer.appendChild(text); if (params.isAnimatable) { text.style.opacity = 0; triggeredOpacity = window.getComputedStyle(text).opacity; text.style.opacity = 1; } }, transitionEndCallback = function (event) { var text = document.createElement('p'); text.innerHTML = 'Transition finished!'; logsContainer.appendChild(text); }; for (paramKey in customParams) { if (customParams.hasOwnProperty(paramKey)) { params[paramKey] = customParams[paramKey]; } } for (transitionKey in transitions) { if (transitions.hasOwnProperty(transitionKey) && logsContainer.style[transitionKey] !== undefined) { detectedTransitionEvent = transitions[transitionKey]; } } logsContainer.classList.add(CLASS_LOGS); document.body.appendChild(logsContainer); elements = [].slice.call(document.querySelectorAll(params.selector)); elements.forEach(function (element) { element.addEventListener('click', clickCallback, false); element.classList.add(CLASS_CLICKER_READY); }); if (params.isAnimatable) { logsContainer.classList.add(CLASS_LOGS_ANIMATABLE); if (detectedTransitionEvent) { logsContainer.addEventListener(detectedTransitionEvent, transitionEndCallback, false); } } }; })(window, window.document); |
Przygotowanie środowiska testowego dla testów jednostkowych JavaScript
Całe środowisko dla testów jednostkowych (unit testów) będzie oparte o następujące narzędzia:
- Gulp.js,
- Karma,
- Mocha i Chai.
1. Gulp.js - narzędzie do automatyzacji zadań
Swego czasu na blogu pisałem o innym narzędziu, które służy do automatyzacji zadań webdeveloperskich - o Grunt.js. Tym razem wykorzystam inne narzędzie, które robi to samo, ale w inny sposób. Główne różnice między tymi dwoma narzędziami to:
- W Grunt.js konfiguracja jest dostarczana jako obiekt JSON, natomiast w Gulp.js konfigurujemy pisząc kod JavaScript. To sprawia, że na pierwszy rzut oka, Grunt.js jest łatwiejszy w konfiguracji.
- Gulp.js wykonuje swoje zadania w oparciu o funkcjonalność node.js o nazwie Stream, która pozwala przetwarzać dane bezpośrednio w pamięci bez zapisywania stanów pośrednich na dysku. Grunt.js poszczególne wyniki przetwarzania plików w poszczególnych etapach zapisuje jako pliki tymczasowe na dysku. Takie podejście sprawia, że Gulp.js jest z reguły szybszy niż Grunt.js.
Gulp.js można zainstalować za pomocą następującej komendy:
npm install gulp --save-dev
Przydatna będzie też linia komend do Gulpa, którą instalujemy za pomocą komendy:
npm install gulp-cli -g
2. Mocha + Chai - framework do testowania kodu JS
Mocha.js jest frameworkiem do pisania testów jednostkowych w JavaScript, który pozwala uruchamiać testy zarówno w przeglądarce jak i na serwerze node.js z poziomu terminala. Dodatkowo, framework Mocha.js jest rozszerzony o bibliotekę Chai.js, która służy pisania warunków sprawdzających w trybie TDD/BDD. Dzięki takiemu zestawowi narzędzi, nasz kod testowy będzie łatwy do pisania i przyjemny do czytania.
npm install mocha chai chai-dom --save-dev
3. Karma - środowisko uruchomieniowe dla testów
Karma jest środowiskiem uruchomieniowym dla testów, to oznacza, że z pomocą tego narzędzia możemy uruchamiać testy z poziomu konsoli bez potrzeby uruchamiania przeglądarki. Dzięki integracji Gulp.js i Karma jesteśmy w stanie szybko dostać informację o stanie naszych testów jednostkowych.
Do narzędzia Karma istnieje szereg pluginów i dodatków które pozwalają nam skonfigurować to narzędzie. W naszym przypadku wykorzystamy następujący zestaw rozszerzeń:
- karma-mocha,
- karma-mocha-reporter,
- karma-chai,
- karma-chai-plugins,
- karma-coverage,
- karma-phantomjs-launcher.
Karma i jego dodatki możemy zainstalować za pomocą następującej komendy:
npm install --save-dev karma karma-mocha karma-mocha-reporter karma-chai karma-chai-plugins karma-coverage karma-chrome-launcher karma-firefox-launcher karma-phantomjs-launcher
Podobnie jak w przypadku Gulp.js przyda się również linia komend do Karmy:
npm install karma-cli -g
Konfiguracja testów w Gulp.js
Pierwszym krokiem który wykonamy będzie urochomienie konfiguratora Karmy za pomocą komendy:
karma init
Z jego pomocą określimy w której przeglądarce będziemy testować nasz kod JS, z którego frameworka testowego będziemy korzystać oraz kilka innych rzeczy. Na koniec powinniśmy otrzymać plik z konfiguracją:
// karma.conf.js
// Karma configuration
// Generated on Wed Dec 30 2015 11:40:36 GMT+0100 (CET)
'use strict';
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: [
'mocha',
'chai',
'chai-dom'
],
// list of files / patterns to load in the browser
files: [
'clicker.js',
'test/clicker-tests.js'
],
// list of files to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'clicker.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: [
'mocha',
'coverage'
],
coverageReporter: {
type: 'html',
dir: 'test/coverage/'
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
});
}
Wygenerowany plik konfiguracyjny musimy uzupełnić o brakujące frameworki testowe: chai
oraz chai-dom
, a także musimy podać odpowiednie ścieżki do pliku z testami oraz do samego pliku z pluginem. Dodatkowo dorzucamy konfigurację dla pokrycia kodu (coverage).
Kolejnym krokiem będzie przygotowanie pliku Gulp.js, który będzie uruchamiał testy:
1 2 3 4 5 6 7 8 9 10 | var gulp = require('gulp'), Server = require('karma').Server, gutil = require('gulp-util'); gulp.task('test', function (done) { new Server({ configFile: __dirname + '/karma.conf.js', singleRun: true }, done).start(); }); |
Kod jest bardzo krótki. Za jego pomocą tworzymy zadanie w Gulp.js które korzysta z narzędzia Karma i do którego przekazujemy ścieżkę do pliku konfiguracyjnego do Karmy.
Tak przygotowany zestaw uruchamiamy za pomocą komendy:
gulp test
W konsoli rezultat testu może wyglądać następująco (zdjęcie z testów dla innego pluginu):
Automatycznie zostanie wygenerowany plik HTML, który pokaże nam stopień pokrycia kodu testami jednostkowymi. Plik będzie dostępny w test/coverage/PhantomJS 1.9.8 (Mac OS X 0.0.0)/index.html
. Po otwarciu tego pliku w przeglądarce internetowej zobaczymy stronę z różnymi statystykami, która może wyglądać podobnie do tego co jest na zdjęciu poniżej:
Podsumowanie
Zdecydowałem się napisać ten artykuł ze względu na fakt, że trudno znaleźć informacje o tym jak testować czysty kod JS, który będzie testował zachowanie pluginu w oknie przeglądarki. Większość przykładów najczęściej dotyczy konfiguracji testów z uwzględnieniem frameworków takich Angular.js, Backbone.js, itd. itp.
Mam nadzieję, że ten artykuł okaże się przydatny dla Ciebie i pomoże podnieść jakość Twojego kodu JS poprzez przygotowania środowiska testowego w którym będziesz mógł/mogła uruchamiać przygotowane przez Ciebie testy jednostkowe w JavaScript.
Jeśli jesteś zainteresowany/a tematyką testów w JavaScript, to daj znać w komentarzach pod tym wpisem. Postaram się wtedy przygotować więcej artykułów dotyczących tego tematu.