Сервис для
сео - оптимизаторов

Найди ошибки на сайте
Ошибки мешают продвижению сайта
Исправь ошибки на сайте
Сайт без ошибок продвигать легче
Получи новых клиентов
Новые клиенты принесут больше прибыль

Введение в модульное тестирование JavaScript

  1. Об авторе
  2. Дальнейшее чтение на SmashingMag:
  3. Сборка юнит-тестов
  4. Сделайте вещи проверяемыми
  5. Рефакторинг, Стадия 0
  6. Тестовый набор QUnit JavaScript
  7. Рефакторинг, 1 этап
  8. Тестирование манипуляции с DOM
  9. Рефакторинг, 2 этап
  10. Вернуться к началу
  11. Заключение

Об авторе

Йорн - независимый веб-разработчик, консультант и тренер, проживающий в Кельне, Германия. Йорн развил набор тестов jQuery в QUnit, модульное тестирование JavaScript… Подробнее о Йорне ...

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

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

Вероятно, это тот случай, когда не используется библиотека JavaScript для некоторой абстракции DOM; писать встроенные обработчики событий гораздо проще, чем использовать API-интерфейсы DOM для привязки этих событий. Все больше и больше разработчиков выбирают библиотеку, такую ​​как jQuery, для обработки абстракции DOM, что позволяет им перемещать эти встроенные события в отдельные сценарии, либо на одной странице, либо даже в отдельном файле JavaScript. Однако размещение кода в отдельных файлах не означает, что он готов для тестирования как единое целое.

Дальнейшее чтение на SmashingMag:

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

Сборка юнит-тестов

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

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

Эта проблема «курица и яйцо» означает, что для добавления тестов в существующий код вы должны рискнуть сломаться. Таким образом, пока у вас нет достаточного охвата юнит-тестами, вам необходимо продолжать тестирование вручную, чтобы минимизировать этот риск.

Этого должно быть достаточно теории на данный момент. Давайте рассмотрим практический пример тестирования некоторого кода JavaScript, который в настоящее время смешан со страницей и подключен к ней. Код ищет ссылки с атрибутами заголовка, используя эти заголовки для отображения, когда что-то было опубликовано, в виде относительного значения времени, например «5 дней назад»:

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры искаженных дат </ title> <script> function prettyDate (time) {var date = new Date (time || ""), diff = ((new Date (). getTime () - date.getTime ()) / 1000), day_diff = Math.floor (diff / 86400); if (isNaN (day_diff) || day_diff <0 || day_diff> = 31) {return; } return day_diff == 0 && (diff <60 && "только сейчас" || diff <120 && "1 минута назад" || diff <3600 && Math.floor (diff / 60) + "минуты назад" || diff < 7200 && "1 час назад" || diff <86400 && Math.floor (diff / 3600) + "часы назад") || day_diff == 1 && "Вчера" || day_diff <7 && day_diff + "days ago" || day_diff <31 && Math.ceil (day_diff / 7) + "недели назад"; } window.onload = function () {var links = document.getElementsByTagName ("a"); for (var i = 0; i <links.length; i ++) {if (links [i] .title) {var date = prettyDate (links [i] .title); if (date) {links [i] .innerHTML = date; }}}}; </ script> </ head> <body> <ul> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <href = "/ 2008/01 / blah / 57 /" title = "2008-01-28T20: 24: 17Z"> 28 января 2008 г. </a> <a href="/john/"> Джон Ресиг </ a > </ small> </ li> <! - больше элементов списка -> </ ul> </ body> </ html>

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

Сделайте вещи проверяемыми

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

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры искаженных дат </ title> <script> function prettyDate (now, time) {var date = new Date (time || ""), diff = (((new Date (now)). getTime () - date.getTime ()) / 1000), day_diff = Math этаж (diff / 86400); if (isNaN (day_diff) || day_diff <0 || day_diff> = 31) {return; } return day_diff == 0 && (diff <60 && "только сейчас" || diff <120 && "1 минута назад" || diff <3600 && Math.floor (diff / 60) + "минуты назад" || diff < 7200 && "1 час назад" || diff <86400 && Math.floor (diff / 3600) + "часы назад") || day_diff == 1 && "Вчера" || day_diff <7 && day_diff + "days ago" || day_diff <31 && Math.ceil (day_diff / 7) + "недели назад"; } window.onload = function () {var links = document.getElementsByTagName ("a"); for (var i = 0; i <links.length; i ++) {if (links [i] .title) {var date = prettyDate ("2008-01-28T22: 25: 00Z", links [i] .title) ; if (date) {links [i] .innerHTML = date; }}}}; </ script> </ head> <body> <ul> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <href = "/ 2008/01 / blah / 57 /" title = "2008-01-28T20: 24: 17Z"> 28 января 2008 г. </a> <a href="/john/"> Джон Ресиг </ a > </ small> </ li> <! - больше элементов списка -> </ ul> </ body> </ html>

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

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

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

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

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры дат с датами </ title> <script src = "prettydate.js"> </ script> <script> window.onload = function () {var links = document.getElementsByTagName ("a"); for (var i = 0; i <links.length; i ++) {if (links [i] .title) {var date = prettyDate ("2008-01-28T22: 25: 00Z", links [i] .title) ; if (date) {links [i] .innerHTML = date; }}}}; </ script> </ head> <body> <ul> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <href = "/ 2008/01 / blah / 57 /" title = "2008-01-28T20: 24: 17Z"> 28 января 2008 г. </a> <a href="/john/"> Джон Ресиг </ a > </ small> </ li> <! - больше элементов списка -> </ ul> </ body> </ html>

Вот содержимое файла prettydate.js:

function prettyDate (now, time) {var date = new Date (time || ""), diff = (((new Date (now)). getTime () - date.getTime ()) / 1000), day_diff = Math этаж (diff / 86400); if (isNaN (day_diff) || day_diff <0 || day_diff> = 31) {return; } return day_diff == 0 && (diff <60 && "только сейчас" || diff <120 && "1 минута назад" || diff <3600 && Math.floor (diff / 60) + "минуты назад" || diff < 7200 && "1 час назад" || diff <86400 && Math.floor (diff / 3600) + "часы назад") || day_diff == 1 && "Вчера" || day_diff <7 && day_diff + "days ago" || day_diff <31 && Math.ceil (day_diff / 7) + "недели назад"; }

Теперь, когда у нас есть что проверить, давайте напишем несколько реальных модульных тестов:

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры дат с датами </ title> <script src = "prettydate.js"> </ script> <script> функциональный тест (тогда ожидаемый) {results.total ++; var result = prettyDate ("2008-01-28T22: 25: 00Z", затем); if (результат! == ожидается) {results.bad ++; console.log («Ожидается» + ожидается + », но был« + результат »); }} var results = {total: 0, bad: 0}; test («2008/01/28 22:24:30», «только сейчас»); test («01.01.2008 22:23:30», «1 минуту назад»); test («2008/01/28 21:23:30», «1 час назад»); тест («2008/01/27 22:23:30», «Вчера»); test («2008/01/26 22:23:30», «2 дня назад»); test («01.01.2007 22:23:30», не определено); console.log ("Of" + results.total + "тесты", + results.bad + "не удалось", + (results.total - results.bad) + "пройдено"); </ script> </ head> <body> </ body> </ html>
  • Запустите этот пример. (Обязательно включите консоль, например Firebug или Chrome Web Inspector.)

Это создаст специальную среду тестирования, используя только консоль для вывода. У него вообще нет никаких зависимостей от DOM, так что вы можете с таким же успехом запускать его в не-браузерной среде JavaScript, такой как Node.js или Rhino, извлекая код из тега script в его собственный файл.

Если тест не пройден, он выдаст ожидаемый и фактический результат для этого теста. В конце он выдаст итоговый отчет с общим, пройденным и пройденным количеством тестов.

Если все тесты пройдены, как и здесь, вы увидите следующее в консоли:

Из 6 тестов 0 не пройдены, 6 пройдены.

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

Ожидаемый 2 дня назад, но был 2 дня назад.

Из 6 тестов 1 не пройден, 5 пройден.

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

Тестовый набор QUnit JavaScript

Выбор каркаса в основном зависит от вкуса. В оставшейся части этой статьи мы будем использовать QUnit (произносится как «q-unit»), потому что его стиль описания тестов близок к нашему специальному тестовому фреймворку.

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры дат с датами </ title> <link rel = "stylesheet" href = "qunit.css" /> <script src = "qunit.js"> </ script> <script src = "prettydate.js"> </ script> <script> test ("основы prettydate" , function () {var now = "2008/01/28 22:25:00"; равно (prettyDate (сейчас, "2008/01/28 22:24:30"), "только сейчас"); равно (prettyDate (теперь «01.01.2008 22:23:30»), «1 минуту назад»); равно (prettyDate (сейчас «01.01.2008 21:23:30»), «1 час назад») ; equal (prettyDate (сейчас, «2008/01/27 22:23:30»), «вчера»); equal (prettyDate (сейчас, «2008/01/26 22:23:30»), «2 дня назад "); равно (prettyDate (теперь," 2007/26/26 22:23:30 "), не определено);}); </ script> </ head> <body> <div id = "qunit"> </ div> </ body> </ html>

Три раздела заслуживают более пристального внимания. Наряду с обычным шаблоном HTML у нас есть три включенных файла: два файла для QUnit (qunit.css и qunit.js) и предыдущий prettydate.js.

Затем есть еще один блок скрипта с реальными тестами. Тестовый метод вызывается один раз, передавая строку в качестве первого аргумента (называя тест) и передавая функцию в качестве второго аргумента (который запустит реальный код для этого теста). Затем этот код определяет переменную now, которая снова используется ниже, а затем несколько раз вызывает равный метод с различными аргументами. Метод равных является одним из нескольких утверждений, которые предоставляет QUnit. Первый аргумент является результатом вызова prettyDate с переменной now в качестве первого аргумента и строкой даты в качестве второго. Второй аргумент, равный ожидаемому результату. Если два равных аргумента имеют одно и то же значение, то утверждение пройдет; в противном случае это не удастся.

Наконец, в элементе body есть некоторая разметка, специфичная для QUnit. Эти элементы являются необязательными. Если он присутствует, QUnit будет использовать их для вывода результатов теста.

Результат таков:

В случае неудачного теста результат будет выглядеть примерно так:

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

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

Утверждения в настоящее время несколько неполны, потому что мы еще не тестировали вариант n недель назад. Прежде чем добавить его, мы должны рассмотреть возможность рефакторинга тестового кода. В настоящее время мы вызываем prettyDate для каждого утверждения и передаем аргумент now. Мы могли бы легко преобразовать это в собственный метод утверждения:

test ("основы prettydate", function () {function date (тогда, ожидается) {равно (prettyDate ("2008/01/28 22:25:00", тогда), ожидается);} date ("2008/01 / 28 22:24:30 "," только сейчас "); дата (" 2008/01/28 22:23:30 "," 1 минута назад "); дата (" 2008/01/28 21:23:30 " , "1 час назад"); дата ("2008/01/27 22:23:30", "Вчера"); дата ("2008/01/26 22:23:30", "2 дня назад"); date ("2007/01/26 22:23:30", не определено);});

Здесь мы извлекли вызов prettyDate в функцию date, вставив переменную now в функцию. В результате мы получаем только соответствующие данные для каждого утверждения, что облегчает их чтение, в то время как основная абстракция остается довольно очевидной.

Тестирование манипуляции с DOM

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

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры дат с датами </ title> <link rel = "stylesheet" href = "qunit.css" /> <script src = "qunit.js"> </ script> <script src = "prettydate2.js"> </ script> <script> test ("prettydate.format ", function () {function date (тогда, ожидается) {равно (prettyDate.format (" 2008/01/28 22:25:00 ", тогда), ожидается);} дата (" 2008/01/28 22: 24:30 "," только сейчас "); дата (" 2008/01/28 22:23:30 "," 1 минута назад "); дата (" 2008/01/28 21:23:30 "," 1 час назад "); дата (" 2008/01/27 22:23:30 "," вчера "); дата (" 2008/01/26 22:23:30 "," 2 дня назад "); дата (" 2007/01/26 22:23:30 ", undefined);}); test ("prettyDate.update", function () {var links = document.getElementById ("qunit-fixture"). getElementsByTagName ("a"); equal (links [0] .innerHTML, "28 января 2008"); равно (links [2] .innerHTML, "27 января 2008 г."); prettyDate.update ("2008-01-28T22: 25: 00Z"); равно (links [0] .innerHTML, "2 часа назад"); равно (links [2] .innerHTML, "Yesterday");}); test ("prettyDate.update, один день спустя", function () {var links = document.getElementById ("qunit-fixture"). getElementsByTagName ("a"); равно (links [0] .innerHTML, "28 января, 2008 "); равно (links [2] .innerHTML," 27 января 2008 "); prettyDate.update (" 2008-01-28T22: 25: 00Z "); равно (links [0] .innerHTML," вчера ") ); равно (ссылки [2] .innerHTML, "2 дня назад");}); </ script> </ head> <body> <div id = "qunit"> </ div> <div id = "qunit-fixture"> <ul> <li class = "entry" id = "post57"> < p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008/01 / blah / 57 /" title = "2008-01-28T20 : 24: 17Z "> 28 января 2008 г. </a> </ span> от <span class =" author "> <a href="/john/"> John Resig </a> </ span> </ small > </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008/01 / blah / 57 /" title = "2008-01-27T22: 24: 17Z"> 27 января 2008 г. </a> </ span> от <span class = "author"> <a href = "/ john /"> Джон Резиг </a> </ span> </ small> </ li> </ ul> </ div> </ body> </ html>

Вот содержимое prettydate2.js:

var prettyDate = {format: function (now, time) {var date = new Date (time || ""), diff = (((new Date (now)). getTime () - date.getTime ()) / 1000 ), day_diff = Math.floor (diff / 86400); if (isNaN (day_diff) || day_diff <0 || day_diff> = 31) {return; } return day_diff === 0 && (diff <60 && "только сейчас" || diff <120 && "1 минута назад" || diff <3600 && Math.floor (diff / 60) + "минуты назад" || diff <7200 && "1 час назад" || diff <86400 && Math.floor (diff / 3600) + "часы назад") || day_diff === 1 && "Вчера" || day_diff <7 && day_diff + "days ago" || day_diff <31 && Math.ceil (day_diff / 7) + "недели назад"; }, update: function (now) {var links = document.getElementsByTagName ("a"); for (var i = 0; i <links.length; i ++) {if (links [i] .title) {var date = prettyDate.format (теперь, links [i] .title); if (date) {links [i] .innerHTML = date; }}}}};

Новая функция prettyDate.update является выдержкой из исходного примера, но с аргументом now, передаваемым в prettyDate.format. Тест для этой функции на основе QUnit начинается с выбора всех элементов в элементе # qunit-fixture. В обновленной разметке в элементе body <div id = «qunit-fixture»>… </ div> является новым. Он содержит фрагмент разметки из нашего исходного примера, достаточный для написания полезных тестов. Помещая его в элемент # qunit-fixture, нам не нужно беспокоиться об изменениях DOM из одного теста, влияющих на другие тесты, поскольку QUnit автоматически сбрасывает разметку после каждого теста.

Давайте посмотрим на первый тест для prettyDate.update. После выбора этих якорей два утверждения проверяют, имеют ли они исходные текстовые значения. После этого вызывается prettyDate.update с фиксированной датой (такой же, как в предыдущих тестах). После этого выполняются еще два утверждения, теперь проверяющих, что свойство innerHTML этих элементов имеет правильно отформатированную дату: «2 часа назад» и «Вчера».

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

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

<! DOCTYPE html> <html> <head> <meta http-equ = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры дат с датами </ title> <link rel = "stylesheet" href = "qunit.css" /> <script src = "qunit.js"> </ script> <script src = "prettydate2.js"> </ script> <script> test ("prettydate.format ", function () {function date (тогда, ожидается) {равно (prettyDate.format (" 2008/01/28 22:25:00 ", тогда), ожидается);} дата (" 2008/01/28 22: 24:30 "," только сейчас "); дата (" 2008/01/28 22:23:30 "," 1 минута назад "); дата (" 2008/01/28 21:23:30 "," 1 час назад "); дата (" 2008/01/27 22:23:30 "," вчера "); дата (" 2008/01/26 22:23:30 "," 2 дня назад "); дата (" 2007/01/26 22:23:30 ", undefined);}); function domtest (name, now, first, second) {test (name, function () {var links = document.getElementById ("qunit-fixture"). getElementsByTagName ("a"); равно (links [0] .innerHTML, «28 января 2008 г.»); равно (links [2] .innerHTML, «27 января 2008 г.»); prettyDate.update (сейчас); равно (links [0] .innerHTML, первое); равно (links [2] .innerHTML, второй);}); } domtest ("prettyDate.update", "2008-01-28T22: 25: 00Z: 00", "2 часа назад", "Вчера"); domtest ("prettyDate.update, один день спустя", "2008-01-29T22: 25: 00Z: 00", "Вчера", "2 дня назад"); </ script> </ head> <body> <div id = "qunit"> </ div> <div id = "qunit-fixture"> <ul> <li class = "entry" id = "post57"> < p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008/01 / blah / 57 /" title = "2008-01-28T20 : 24: 17Z "> 28 января 2008 г. </a> </ span> от <span class =" author "> <a href="/john/"> John Resig </a> </ span> </ small > </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008/01 / blah / 57 /" title = "2008-01-27T22: 24: 17Z"> 27 января 2008 г. </a> </ span> от <span class = "author"> <a href = "/ john /"> Джон Резиг </a> </ span> </ small> </ li> </ ul> </ div> </ body> </ html>

Здесь у нас есть новая функция domtest, которая инкапсулирует логику двух предыдущих вызовов test, вводя аргументы для имени теста, строки даты и двух ожидаемых строк. Затем он вызывается дважды.

Вернуться к началу

Имея это в виду, давайте вернемся к нашему первоначальному примеру и посмотрим, как это выглядит сейчас, после рефакторинга.

<! DOCTYPE html> <html> <head> <meta http-lim = "Content-Type" content = "text / html; charset = UTF-8" /> <title> Примеры окончательной даты </ title> <script src = "prettydate2.js"> </ script> <script> window.onload = function () {prettyDate.update ("2008-01-28T22: 25: 00Z"); }; </ script> </ head> <body> <ul> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z"> <span> 28 января 2008 г. </ span> </a> < / span> от <span class = "author"> <a href="/john/"> John Resig </a> </ span> </ small> </ li> <li class = "entry" id = " post57 "> <p> бла-бла-бла… </ p> <small class =" extra "> Опубликовано <span class =" time "> <a href =" / 2008/01 / blah / 57 / "title =" 2008 -01-27T22: 24: 17Z "> <span> 27 января 2008 г. </ span> </a> </ span> от <span class =" author "> <a href="/john/"> John Resig </a> </ span> </ small> </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Добавлено < span class = "time"> <a href="/2008/01/blah/57/" title="2008-01-26T22:24:17Z"> <span> 26 января 2008 г. </ span> </ a > </ span> по <span class = "author"> <a href="/john/"> John Resig </a> </ span> </ small> </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008/01 / bl ах / 57 / "title =" 2008-01-25T22: 24: 17Z "> <span> 25 января 2008 г. </ span> </a> </ span> от <span class =" author "> <a href = "/ john /"> Джон Резиг </a> </ span> </ small> </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> < small class = "extra"> Опубликовано <span class = "time"> <a href="/2008/01/blah/57/" title="2008-01-24T22:24:17Z"> <span> 24 января , 2008 </ span> </a> </ span> от <span class = "author"> <a href="/john/"> John Resig </a> </ span> </ small> </ li > <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008 / 01 / blah / 57 / "title =" 2008-01-14T22: 24: 17Z "> <span> 14 января 2008 г. </ span> </a> </ span> от <span class =" author "> < a href = "/ john /"> Джон Резиг </a> </ span> </ small> </ li> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p > <small class = "extra"> Опубликовано <span class = "time"> <a href="/2008/01/blah/57/" title="2008-01-04T22:24:17Z"> <span> 4 января 2008 г. </ span> </a> </ span> от <span class = "author"> <a href="/john/"> John Resig </a> </ span> </ small> < / л i> <li class = "entry" id = "post57"> <p> бла-бла-бла… </ p> <small class = "extra"> Опубликовано <span class = "time"> <a href = "/ 2008 / 01 / blah / 57 / "title =" 2007-12-15T22: 24: 17Z "> <span> 15 декабря 2008 г. </ span> </a> </ span> от <span class =" author "> <a href="/john/"> Джон Ресиг </a> </ span> </ small> </ li> </ ul> </ body> </ html>

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

Заключение

Тестирование кода JavaScript - это не просто использование какого-либо бегуна и написание нескольких тестов; обычно это требует некоторых серьезных структурных изменений применительно к коду, который до этого тестировался только вручную. Мы рассмотрели пример того, как изменить структуру кода существующего модуля для запуска некоторых тестов, используя специальную среду тестирования, а затем заменив ее более полнофункциональной средой для получения полезных визуальных результатов.

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

(ал) (км)