Tag: tests

Sequelize и тестирование

Добрался до тестирования серверной части (node). Встал вопрос: а как проще всего протестировать вещи, связанные с БД? Подумал о том, что должен быть для Sequelize какой-нибудь драйвер, который отрабатывает прямо в ОЗУ. Сразу не нашёл, но зато нашёл вариант с sqlite и storage=memory. Сработало. В купе с хаками для mocha в виде beforeEach и afterEach, получилось довольно удобно. Работает молниеносно, подключается тривиально. Единственное ― приходится тягать пакет с sqlite тоже. В моём случае это оказалось совершенно не критичным.

{ dialect: 'sqlite', storage: ':memory:', }

Конфигурация mocha и mocha.opts

Случайно наткнулся на то, что можно вынести command line parameters для консольного запуска mocha в отдельный файл test/mocha.opts. Но тут же наткнулся на грабли. Файл должен лежать именно в test/mocha.opts, а у меня директория тестов называлась tests, и этого хватило. Т.е. механизм поиска файла сильно уступает аналогичному для, скажем, .eslintrc или .gitignore. Сам формат файла простой, пример:

--require test/setup.js
--compilers js:babel-core/register

Теперь mocha можно запускать без параметров. Интересная выресовывается картинка, много разных вспомогательных файлов: mocha.opts, .babelrc, .eslintrc, .gitignore, webpack.config.js, README.md, package.json и пр. шушера. А ведь не так давно мне было привычно размещать исходники прямо в корневой директории проекта, теперь же нужно им отдельную директорию создавать.

  • Во-первых чтобы они не утонули среди вспомогательных файлов
  • Во-вторых т.к. всё равно нужно куда-то итоговую сборку собирать
  • В третьих таким образом удобно избегать node_modules (к примеру для поиска по файлам проекта)

Написание тестов для React с использованием enzyme

Сразу отмечу, что статья не претендует на истину. Это всего лишь мои заметки о том, как вообще можно написать и запустить простые тесты для React, т.к. тут оказалось много места где заблудиться\окопаться. Итак, начнём.

Babel

Раз React значит и JSX, а значит и babel. Но простой запуск mocha просто запускает nodejs, в котором никакого JSX нет (и наверное никогда не будет). Что делать? Поиск подсказывает, что нужно запускать mocha (далее речь идёт именно о mocha) с флагом –compilers js:babel-core/register. Что сие есть? Это флаг заставит mocha предварительно компилировать код используя babel-core/register, который грубо <s>изнасилует</s>подменяет require() в nodejs, что позволяет ему предварительно изуродоскомпилировать кодовую базу в нормальный javascript

Import\Export и .babelrc

Ок, но как babel-ю подсказать, что использовать в качестве конфига? Точного внятного ответа пока дать не могу, но как минимум можно создать в корне json-файл с именем .babelrc, который будет содержать json-конфиг. Мне прокатило, но в более сложных ситуациях нужно будет искать вариант поделикатнее. Собственно, чтобы не дублировать конфиг, имеет смысл подключить его и в webpack.config.js. Сразу отмечу, что взять и распарсить .babelrc как json простым вызовом require() nodejs почему-то не смогла (видимо мешает отсутствие расширения .json), поэтому fs.readFileSync и JSON.parse вам в помощь. 

Полифилы, JSDom и прочая нецензурщина

nodeJS это nodeJS, а не браузер, а для ряда тестов потребуется имитация браузера. И похоже, что на данный момент изящных решений нет, т.к. кодовая база react-* грубо сверяется с глобальным наличием window &amp; document. Мде. Помимо прочего всякие полифилы, вроде regenerator-а (ну или отключить транспайлинг async-await в генераторы ещё до этапа компиляции) нужны. 

Для решения вопроса с полифилами достаточно import ‘babel-polyfill’. а вот с JSDom так просто не срастётся. Я воспользовался таким решением:

  • import ‘jsdom-global/register’;
  • принудительно зафиксировал jsdom на 8-й версии (а не 10-й)

Замечу, что жизненноважно подключать этот костыль с JSDom до подключения React, иначе груды неадекватных глупых ошибок без внятных описаний украсят вашу безмятежную жизнь. Суть в том, что в недрах разных react-либ window кешируется ещё на этапе подключения файлов. И в последствии какой-нибудь флаг вида canUseDom === false будет вам яростно мешаться.

Можно подключать эти либы в каждом тест-файле, но тогда вы столкнётесь с тем, что при запуске сразу пачки тестов, babel-polifil будет падать со словами “я уже подключён”, jsdom тоже будет что-нибудь базлать. Разумная альтернатива вынести обе эти строки в некий setup.js, а потом его подключать флагом –require tests/setup.js.

Сами тесты

React поставляет в пакете react-dom ряд инструментов для тестирования. Но даже на официальной странице этих инструментов упоминается, что вам стоит воспользоваться некой либой под названием enzyme. Установка оказалась не совсем тривиальной, т.к. сильно зависит от версии вашего React-а. Очень внимательно прочитайте инструкции по установке. Заодно доставьте к нему что-нибудь вроде chai для удобства тестирования.

Отмечу, что enzyme предоставляет по сути 3 набора утилит для работы с вашими React компонентами. А именно shallow для быстрого и поверхностного рендеринга, render для работы уже с html, и mount для полноценной работы с DOM и life-cycle React-компонентов. В любых маломальски сложных ситуациях готовьтесь к танцам с бубнами. Например .simulate метод для вызова событий умеет только React-события, а повешанные через addEventListener уже нет. Готовьтесь к нефинормативным ошибкам, куда же без них. 

Примеры кода для тестов можно посмотреть здесь. По правде говоря я, на данный момент, не в восторге ни от самой либы, ни от её документации. 

CasperJS, PhantomJS, SlimerJS

SlimerJS ― это Firefox для автоматических тестов или же каких-либо подручных задач. PhantomJS ― это webkit браузер без окна, для тех же целей. CasperJS ― это test-кит, обёртка над PhantomJS и SlimerJS. До недавнего времени CasperJS был присмерти, основной разработчик на него забил и никто проектом не занимался. Но судя по коммитам дело сдвинулось с мёртвой точки, и проект скорее жив, чем мёртв. Появилась поддержка свежих версий PhantomJS, которую я так давно ждал. 

PhantomJS

С ним у меня возникло множество проблем. Дело в том, что webkit в нём очень древний. Он не способен переварить less.js, нуждается в CSS-префиксах к flexbox, не знает ничего о Function.Prototype.bind. В общем less приходится билдить в CSS, а для JS пихать es5-shim и babel-polifill. Про ES2015 молчу :-)

Стабильностью не страдает, и регулярно падает с PhantomJS has crashed, без объяснения причин. Плохо дружит с jQuery.IndexedDB, с некоторой периодичностью выкидывая ошибки.

Не имеет собственного окна, что сильно затрудняет отладку тестов или персональных косяков его самого. Но умеет скриншоты, что несколько выручает. А ещё мне пока не удалось завести в нём contenteditable (обновился до 2.1 и завелось).

Касательно работы через CasperJS… Почему-то запускает только 1-ый тест в файле. Много отличий в работе с require по сравнению со SlimerJS. Если ваша тестовая база подбита под SlimerJS, то потребуется много работы напильником, прежде чем тесты хотя бы запустятся.

CSS. Свежие версии фантома умеют зачатки flexbox-а. С префиксом -webkit и сильно устаревшими свойствами. По сути половину не умеет. Насколько я понял, исходя из беглого поиска, там QWebKit 2.2, который сильно устарел. Его возможности примерно равны Safari 5.1. Открыл я в ней проект… поковырялся, понял что безнадёга полнейшая и пришлось от фантома отказаться. Буду ждать обновлений.

В отличии от SlimerJS, фантом умеет падать с правильным exit code, а также умеет работать по file:// протоколу с AJAX запросами, что очень удобно для frontend тестирования.

Отладка. Если кратко ― ад. К примеру console.log для объектов выводит [Object object]. Любит при eval уходить в вечные циклы. Stacktrace-ы выдаёт далеко не всегда. Если запускаемый файл содержит ошибки синтаксиса ― где именно не укажет. Ну и т.д… Обычно причину приходится искать методом проб и ошибок.

UPD: отдельные граблки это то каким образом casperJS гоняет данные между тестом и страницей. В случае slimerJS, главное чтобы эти данные могли сериализовываться, к примеру, JSON-ом. В случае phantomJS начинаются какие-то чудеса с null и похожими значениями. Разбираться в чём дело я не стал, а критичные к таким багам данные стал вручную перед отправкой перегонять в JSON-строку, а в тесте уже распаковывать.

SlimerJS

В отличии от фантома не умеет работать без окна. В Linux эту проблему можно решить, к примеру, так: xvfb-run %команда%, без каких-либо побочных эффектов. 

Очень многие вещи, такие как require, понимает не так, как их понимает PhantomJS. По сути то тут, то там, приходит ставить условие phantom.casperEngine === ‘slimerjs’, что сильно поело нервов.

В отличии от фантома умеет кое-что и из ES2015, и никакие shim-es5 ему не нужны. Без труда переварил less.js. Отлично справился с flexbox-ом. 

Пока не умеет опции –web-security=no, что не позволяет нормально тестировать по file:// протоколу, т.к. не проходят AJAX запросы.

Всегда завершает скрипт с кодом 0, что создаёт проблемы в определении, провалился ли тест, или был успешен, к примеру, из grunt-задачи.

Отладка примерно столь же ужасная, что и в фантоме. Тоже умеет скриншоты.

CasperJS

Располагает интересным API. К примеру может загружать файл в input[type=file], умеет [contenteditable] и курсоры. Умеет дожидаться появления или исчезновения HTMLElement-а по его selector-у и многое другое.

Но требует слишком много возни с подключением скриптов, сами тесты приходится писать без ES2015 возможностей. Располагает ужасной отладкой. API не связанное с самими web-страницами очень проблематично работает, к тому же по разному в зависимости от движка. К примеру, подключить к тесту какой-нибудь lodash может оказаться непростой задачей.

Test-kit очень сильно отличается от привычных мне подходов (таких как в mocha). Я так и не понял, как удобно организовать большие и сложные тесты. Вывод в консоль в итоге напоминает какой-то хаос.

Резюмируя

Инструментарий есть, но он ужасен. Требует очень большого и длительного внимания. Работает весьма нестабильно. По сути это сплошная головная боль. А сами проекты почти не развиваются. Увы.

Плагин для grunt, оказался чуть менее, чем никаким, ввиду чего пришлось писать собственную реализацию, которая пока тоже далека от желаемой.