Пару месяцев назад я писал пост, о том как научить webpack для spa. С того момента инструмент шагнул вперед и оброс дополнительным количеством плагинов, а так же примерами конфигураций.
В этой статье хочу поделиться опытом смешивания гремучей смеси webpack + jasmine + chai + karma. В лучшей, по-моему мнению, книге про автоматизированное тестирование Christian Johansen — Test-Driven JavaScript Development — обозначены проблемы, с которыми разработчик сталкивается при написании кода без тестов:
— Код написан, но поведение не доступно в браузере (пример .bind () и IE 8); — Имплементация изменена, но совокупность компонентов приводит к ошибочному или не рабочему функционалу; — Новый код написан, нужно позаботиться о поведении со старыми интерфейсами.
Опираясь на опыт, скажу. Программисты, избравшие путь самурая TDD(Test-driven development ), тратят много времени на покрытие кода тестами. В итоге остаются в выигрыше на этапе тестирования и отлавливания багов.
Глоссарий
— Webpack — модульный сборщик ассетов; — Karma — test-runner для JavaScript; — Jasmine — инструмент для определения тестов в стиле BDD; — Chai — библиотека для проверки условий, expect, assert, should;
Установка пакетов
Для начала, приведу список пакетов, которые дополнительно устанавливаем в проект. Для этого воспользуемся npm.
#tools
npm i chai mocha phantomjs-prebuilt --save-dev
#karma packages #1
npm i karma karma-chai karma-coverage karma-jasmine --save-dev
#karma packages #2
npm i karma-mocha karma-mocha-reporter karma-phantomjs-launcher --save-dev
#karma packages #3
npm i karma-sourcemap-loader karma-webpack --save-dev
Идем дальше.
Настройка окружения
После установки дополнительных пакетов, настраиваем конфигурацию karma. Для этого в корне проекта создадим файл karma.conf.js
Опыт показывает, что спеки (от англ spec — specification) удобнее хранить в тех же папках, что и тестируемые компоненты. Хотя, конечно же, Вы сами строите архитектуру своего приложения. В примере ниже, Вы встретите единственный для ознакомительной статьи пример теста, который расположен в директории tests модуля boilerplate.
Такое именование директорий дает позитивный отклик от новых разработчиков, желающих ознакомиться с функционалом модуля или компонента.
TL; DR открывая проект, мы видим папку со спецификациями, расположенную на первом месте за счет строковой сортировки.
Запуск
Тут ничего нового. Для старта я использую встроенный функционал npm секции scripts. Ровно так же как и для dev-server и «боевой» сборки функционала.
Чтобы запустить тесты в режиме «обновляй при изменении», в корне проекта набираем команду:
npm run test:watch
Для разового запуска:
npm run test:single
Первый тест
Для примера, предлагаю рассмотреть нетривиальную с точки зрения unit тестирования задачу. Обработка результата работы Backbone.View. Ничего страшного, если первый тест выглядит формальностью.
Ожидается, что при создании экземпляра View, будет вызвана функция render (). Результатом которой станет html — декларированный в шаблоне hello.jade
Пример формального теста покрывающего функционал:
// boilerplate.spec.js
'use strict';
const $ = require('jquery');
const Module = require('_modules/boilerplate');
describe('App.modules.boilerplate', function() {
// подготовим переменные для использования
let $el = $('
', { class: 'test-div' });
let Instance = new Module($el);
// формальная проверка на тип возвращаемой переменной
it('Should be an function', function() {
expect(Module).to.be.an('function');
});
// после применения new на функции конструкторе - получим объект
it('Instance should be an object', function() {
expect(Instance).to.be.an('object');
});
// инстанс должен содержать el и $el свойства
it('Instance should contains few el and $el properties', function() {
expect(Instance).to.have.property('el');
expect(Instance).to.have.property('$el');
});
// а так же ожидаем определенной функции render()
it('Instance should contains render() function', function() {
expect(Instance).to.have.property('render').an('function');
});
// $el должен содержать dom element
it('parent $el should contain rendered module', function() {
expect($el.find('#fullpage')).to.be.an('object');
});
});
Запускаем тестирование и наблюдаем за результатом.
В дополнении ко всему, директория tmp/coverage/html-report/ будет содержать отчет о покрытии кода:
Вывод
Тесты, даже в таком формальном виде, избавят нас от обязательств перед собой. Применив достаточную изобретательность в их декларации, мы можем уберечь себя и коллег от головной боли.
В заключении, представьте то количество времени, которое мы ежедневно тратим на каждую итерацию: «изменил — сохранил — обновил браузер — увидел результат». Очевидное рядом. Тестирование — полезный инструмент на страже Вашего времени.