Hi there! Our project relies on ads or donation to keep the site free to use. Please sending a donation . Thanks!
Подождите, пожалуйста, выполняется поиск в заданном разделе

Введение в unit тестирование JavaScript кода

Привет, сегодня поговорим про unit тестирование javascript, обещаю рассказать все что знаю. Для того чтобы лучше понимать что такое unit тестирование javascript , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки

Довольно распространенная проблема при написании модульных тестов для клиентского кода состоит в том, что его структура не подходит для тестирования. Ведь JavaScript код может быть написан для любой страницы сайта или модуля в приложении, также он может быть непосредственно связан с серверной логикой и с HTML кодом. В самом худшем случае код полностью привязан к HTML в качестве встроенных обработчиков событий.

Обычно такая ситуация происходит, когда разработчик для написания приложения не использует специальные JavaScript библиотеки. Еще бы, ведь написать встроенный обработчик события намного легче, чем привязать его же через DOM API. Однако большинство разработчиков все же используют специальные JavaScript библиотеки, например JQuery. JQuery позволяет помещать встроенные обработчики в отдельные сценарии или на той же странице, или в отдельном js файле. Но код, размещенный в отдельном файле, не является готовым к тестированию модулем.

Что же такое модуль? В лучшем случае это функция, которая всегда возвращает некоторый результат для некоторого параметра. Такой модуль тестировать очень легко. Конечно для выделения таких функций из существующего скрипта большую часть времени придется потратить на операции с деревом DOM. Однако такой подход поможет выделить из кода структурные единицы для создания модульных тестов.

Создание модульных тестов

Легче всего тестировать скрипт, если пишешь его с нуля. Но статья не об этом. В данной статье я постараюсь рассказать о том, как извлекать и тестировать наиболее важные части скрипта, а также выявлять и устранять ошибки.

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

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

Достаточно теориии! Разберу-ка я практический пример, тестируя конкретный JavaScript код, который встроен в html код страницы. Скрипт просматривает все ссылки с аттрибутом title. И использует значение этого аттрибута для преобразования текста ссылок в более человеческий вид: «n minutes ago», «n hours ago», «n weeks ago».

  • Введение в unit тестирование JavaScript кода

Если вы запустите этот скрипт, то увидите проблему: ни одна дате не заменилась. Хотя код при этом работает. Он проходит по всем ссылкам a и проверяет, есть ли у них title. Если он есть, то скрипт передает его значение функцие prettyDate. Если же она возвращает что-то, то скрипт заменяет innerHTMLссылки на этот результат.

Преобразование кода в тестируемый вид

Проблема в этом скрипте в том, что функция prettyDate для любой даты старше 31 дня возвращает undefined (с помощью return), оставляя тескт ссылки без изменений. А текущая дата намного старше 31-го дня. Поэтому, чтобы скрипт изменил текст ссылок, я жестко указал текущую дату:

Введение в unit тестирование JavaScript кода

В результате работы скрипта, появились ссылки: «2 часа назад», «вчера» и так далее. Это уже кое-что, но до тестируемого модуля не дотягивает. Поэтому, без дальнейшего рефакторинга все, что я могу делать, это протестировать изменения в разметке.

При этом нужно создавать код с максимально чисто функцией (методами) , для того чтобы тесты былиь изолированы от окружения (БД, сеть, файловая система, время).

Рефакторинг , стадия 0

По всей видимости скрипт нуждается в 2 изменениях:

  • Передача даты в функцию prettyDate (что уже сделано);
  • Выделение функции prettyDate в отдельный файл, чтобы его можно было подключить в страницу для модульного тестирования.
Введение в unit тестирование JavaScript кода
Содержимое файла prettydate.js:
Введение в unit тестирование JavaScript кода

Теперь можно писать модульный тест.


 

Введение в unit тестирование JavaScript кода

(Перед запуском убедитесь, что у вас включена консоль, например Firebug или Chrome’s Web Inspector.)

В результате получился модульный тест, который выводит результаты в консоль . Об этом говорит сайт https://intellect.icu . Это позволяет избежать зависимости от DOM, поэтому тест можно запустить в небраузерной JavaScript среде, например Node.js или Rhino.

Скрипт выведет в консоль суммарную информацию: общее количество тестов, количество неудачных тестов, количество удачных тестов.

При успешном прохождении всех тестов, результат будет следующего вида:

Of 6 tests, 0 failed, 6 passed.

Если один из тестов не прошел, то он выведен в консоль в таком виде:

Expected 2 day ago, but was 2 days ago.
Of 6 tests, 1 failed, 5 passed.

Данный подход к разработке модульных тестов интересен как доказательство концепции: гораздо более практично использовать существующий фреймворк для тестирования, который обеспечит лучшую производительность и больше возможностей для написания и организации тестов.

Среда QUnit для тестирования JavaScript кода

Выбор фреймворка для тестирования является делом вкуса.

Я предпочитаю использовать QUnit, потому что у него много возможностей

Введение в unit тестирование JavaScript кода

Здесь стоит обратить внимание на 3 секции. Здесь подключены 3 файла: 2 файла это QUnit (qunit.css and qunit.js) и 1 файл prettydate.js.

Затем идет блок JavaScript кода с тестовыми данными. Метод test вызывается только один раз, передавая в качестве первого аргумента имя теста, а вторым аргментом является функция, которая фактически запускает тесты. В этом блоке объявляется переменная now, которая используется при вызове метода equal. Метод equal — это один из методов, которые предлагает QUnit. В качестве первого аргумента методу equal передается результат функции prettyDate. В качестве второго аргумента методу equal передается ожидаемый результат. Если эти два аргумента будут одинаковыми, то утверждение истинно, иначе не истинно.

При успешном тесте QUnit выведет результат следующего вида:

Введение в unit тестирование JavaScript кода

Если во время тестирования было не верное утверждение, то результат будет такого вида:

Введение в unit тестирование JavaScript кода

Поскольку на втором скриншоте один из тестов был неудачным, то QUnit не сворачивает лог тестирования и можно сразу увидеть, что пошло не так. Вместе с выводом ожидаемых и фактических результатов QUnit также показывает разницу между ними, которая очень полезна при сравнении больших строк. Здесь же довольно очевидно, что пошло не так.

Рефакторинг, шаг 1

Тестовых данных недостаточно, т.к. я не тестирую вариант n weeks ago. Но сначала я снова сделаю рефакторинг. Функция prettyDate вызывается для каждого утверждения и каждый раз передается аргумент now. Сейчас я это исправлю:

Введение в unit тестирование JavaScript кода

Теперь функция prettyDate вызывается внутри новой функции date и больше не создается переменная now, ее значение жестко прописано в качестве параметра.

Тестирование операций с DOM деревом

Я уже достаточно хорошо поработал с функцией prettyDate. Теперь обратим внимание на первоначальный скрипт. В скрипте делалась выборка некоторых DOM-элементов, и они обновлялись в зависимости от результата функции prettyDate. Применяя те же принципы как и прежде, я сделаю рефакторинг и оттестирую его как следует) Кроме того, я создам модуль для этих 2 функций, и, чтобы избежать ошибок с глобальным пространством имен, дам им более осмысленные имена.

Введение в unit тестирование JavaScript кода

Содержание prettydate2.js:

 Введение в unit тестирование JavaScript кода

Из базового скрипта я выделил новую функцию prettyDate.update, однако аргумент now все еще передается в prettyDate.format. Базовый QUnit-тест для этой функции начинается с отбора всех элементов a из блока div с идентификатором #qunit-fixture. В HTML коде появился новый элемент

. В нем содержится извлеченная разметка из начального примера, необходимая для тестирования. Помести ее в #qunit-fixture можно не беспокоиться о влиянии одного теста на результат другого, т.к. QUnit автоматически сбрасывает разметку после каждого теста.

Что происходит при первом тестировании prettyDate.update.

Сначала выбираются ссылки из

, далее они проверяются на содержание дат. Затем вызывается prettyDate.update, передавая фиксированную дату (такую же как и в предыдущих тестах). Потом происходит проверка текста этих же ссылок на желаемые значения «2 hours ago» и «Yesterday», т.к. prettyDate.update должен был заменить этот текст.

Рефакторинг, шаг 2

Следующий тест prettyDate.update, one day later делает почти тоже самое, за исключением того, что передает другую дату в prettyDate.update и потому возвращает другой результат для 2 ссылок.

Настало время для очередного рефакторинга! Необходимо убрать дублирование!

Введение в unit тестирование JavaScript кода

В этом примере я создал новую функцию domtest, которая инкапсулирует логику двух предыдущих вызовов для проверки и передает такие аргументы как имя теста, дату в строковом формате и две ожидаемые на выходе строки. Эта функция вызывается дважды.

Что произошло?

Надо бы посмотреть, что получилось в итоге)

Введение в unit тестирование JavaScript кода



В результате рефакторинга было сделано множество усовершенствований по сравнению с первым примером. И благодаря модулю prettyDate я могу добавить еще больше функциональности без конфликтов в глобальном пространстве имен.

Заключение

При тестировании JavaScript кода возникает вопрос не только в том, какой выбрать фреймворк или написать модуль самостоятельно. Обычно приходится делать большие структурные изменения для их применения к скрипту, который прежде проверялся вручную. Я очень надеюсь, что вы поняли хоть что-нибудь. Например, как изменять структуру существующего модуля для тестирования и как использоваеть более функциональный фреймворк, для получения более полезных результатов.

У фреймворк QUnit намного больше возможностей, чем я описал. Например, есть поддержка тестирования асинхронного кода: AJAX и события, тайм-ауты. Визуализация результатов помогает отлаживать код, облегчает повторный запуск специфичных тестов и предусматривает стек вызовов для неудачных утверждений и захваченных исключений. Для дальнейшего изучения QUnit фреймворка пройдите по ссылке QUnit Cookbook.

Понравилась статья о unit тестирование javascript? Откомментируйте её Надеюсь, что теперь ты понял что такое unit тестирование javascript и для чего все это нужно, а если не понял, или есть замечания, то нестесняся пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятелно рекомендую изучить комплексно всю информацию в категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки

Комментарии (4)

avatar
Admin 5.5.2020 11:25

Обычно я пишу свои JS-модули, используя шаблон модуля Revealing Module, т. Е. Выставляю только одни методы, оставляя другие внутренними для объекта. Я пытаюсь интегрировать написание тестов (в частности, jasmine.js) в свой рабочий процесс разработки, но до сих пор не могу понять, как применять тесты к моему частному коду. Был ли у вас опыт с этим? Во всех примерах, которые я видел, все тестируемые функции общедоступны.

avatar
Admin 5.5.2020 11:26

PS. Пожалуйста, обновите ссылку для QUnit Cookbook (должно быть http://qunitjs.com/cookbook/ )

avatar
Admin 5.5.2020 11:27

В настоящее время я участвую в модульном тестировании графического интерфейса, и с такими тяжеловесными вещами, как Java Swing, я в порядке, но мне тоже нужно что-то для мира HTML5 / Ajax / JS, и этот QUnit, кажется, является хорошим началом для меня. Идея хорошо понятна и позволяет даже новичку начать модульное тестирование кода JS. Отличная работа. Там только одна записка. ИМХО, «Рефакторинг, Стадия 1 скорее скрывает смысл, чем облегчает его понимание. Эта функция «дата не дает никакой реальной ценности, она просто мешает «сейчас куда-то еще. Я не уверен, что я даже ввел бы локальную переменную сейчас , и я почти уверен, что никогда не реорганизовал бы ее. Первая версия гораздо более читабельна для меня, и я предлагаю другим подумать, почему мы должны сделать тесты короче. Чем легче понять тест, тем лучше, особенно если сначала нужно написать тесты, а разрабатывать код только потом (разработка через тестирование или, что еще лучше, разработка через поведение). Насколько я вижу, имеет общее предположение, что он «нечитаемый и, следовательно, трудно поддерживаемый, в основном потому, что многие эксперты JS «сокращают свой код (то есть обфусцируют его).

avatar
Admin 5.5.2020 11:27

Существуют и другие структуры для написания модульных тестов: https://github.com/moll/js-must https://github.com/visionmedia/should.js/ http://unitjs.com http: // nodejs .org / api / assert.html и многие другие


avatar

Чтобы оставить комментарий войдите или зарегистрируйтесь



Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки