четверг, 26 ноября 2009 г.

Модульное тестирование javascript кода при помощи JsTestDriver

Бочка мёда

Хочу порекомендовать JsTestDriver - великолепный фрэймворк для модульного тестирования (unit testing) javascript кода от ребят из Google. Он позволяет в считанные доли секунды протестировать javascript-код одновременно на любом (ну, почти любом) количестве любых браузеров с любых компьютеров и платформ. Впечатляет, не правда ли?

Идея JsTestDriver проста, как всё гениальное (картинка взята с оф. сайта):


Запускается небольшой веб-сервер, причем не обязательно его запускать на локальной машине. К нему, как и к обычному веб-серверу, используя определённый URL (например, http://localhost:4224/capture), коннектятся необходимые для тестирования браузеры, которые могут находится на любом компьютере (и, соответственно, платформе), с которого доступен сервер. Таким образом, можно будет одновременно тестировать код на Safari под Mac OS, Internet Explorer под Windows и, например, Firefox под Linux.

Далее происходит основная магия.
Браузеры (slaves в терминологии JsTestDriver) начинают периодически опрашивать сервер (при помощи AJAX) в ожидании новых задач (тестов). И, как только задание будет получено, браузеры загрузят необходимые скрипты, выполнят тестовые функции (test cases) и вернут на сервер результаты выполнения. Управление же сервером происходит при помощи клиентского приложения, которое легко интегрируется как с IDE разработчика, так и с системами автоматической сборки проектов (Ant, Team City, Phing и т.п.).

Однако, разработчику важна не столько вся эта кухня, сколько возможность иметь под рукой удобный инструмент для лёгкого и быстрого запуска модульных тестов и отслеживания результатов. В этом нам поможет JsTestDriver плагин для Eclipse (аналогичный плагин есть так же и для IntelliJ IDEA).



Не вдаваясь в подробности опишу лишь основные фичи этого плагина:
  • Минимум настроек: только номер порта для сервера и пути к файлам браузеров (Firefox, Chrome, Safari, Internet Explorer и Opera) на локальном компьютере
  • Запуск сервера и браузеров для тестирования прямо из IDE
  • Простейший конфигурационный файл для запуска тестов (прописывается только URL тестового сервера и список необходимых javascript-файлов)
  • Ручной запуск модульных тестов через меню Run
  • Автоматический запуск модульных тестов при каждом изменении javascript-файлов
  • Наглядное отображение результатов тестирования прямо в IDE (на каком браузере под какой платформой в каком тест-кейсе произошла ошибка)
В общем, установка и настройка JsTestDriver не вызывают никаких особых проблем (всё очень подробно описано на оф. сайте), а уж использование приносит только сплошное удовольствие. Правда, если быть честным, для этого пришлось немного (совсем чуть-чуть :) "поработать напильником"...

Капля дёгтя

Дело в том, что текущая версия (1.2 для сервера и 1.0.6 для плагина) немного недоработана (уверен, что это не надолго). В частности, если в каком либо тестируемом js-файле будет синтаксическая ошибка (или иная ошибка, возникающая при исполнении этого файла), то в результатах тестов это никак не отражается. Кроме того, наблюдается некоторая глючность Opera при исполнении тестов.

Лекарство

Для решения проблемы пришлось переписать один метод по загрузке скриптов и добавить "системный" тест-кейс, который будет отражать ошибки, произошедшие до начала выполнения пользовательских тест-кейсов (см. runner-fix.js). Ну и, в дополнение, мне показалось уместным добавить ещё две вещи:
  1. Обновление тестовой страницы после выполнения тестов, чтобы исключить возможность влияния предыдущего выполнения тестов на последующие
  2. assertFail( [expectedErrorMessage], caseWrapper ) - функция, которая позволяет протестировать случаи, когда должна происходить ошибка. Пример использования:
      function doSomething() {
          throw new Error( 'Some error' );
      }
      ...
    
      // Test case
      assertFail( 'Some error', function(){ doSomething() } );
      // true
      
Чтобы внедрить эти изменения в JsTestDriver нужно выполнить следующие шаги:
  1. Загружаем архив (содержит три файла: runner-fix.js, Runnerquirks.html, Runnerstrict.html)
  2. Находим JsTestDriver.jar из которого запускается сервер. Для плагина к Eclipse он находится где-то в папке configuration. В частности у меня, вот тут:
    c:\Program Files\Eclipse\Galileo\configuration\org.eclipse.osgi\bundles\929\1\.cp\lib\
  3. Открываем JsTestDriver.jar как обычный zip-архив
  4. Заходим в папку JsTestDriver.jar\com\google\jstestdriver\javascript\
  5. Копируем туда файлы из runner-fix.zip
  6. Запускаем JsTestDriver (вручную или через Eclipse плагин) и наслаждаемся :)
Подробнее о том, как собственно устанавливать, настраивать и использовать сервер или плагин, или писать тест-кейсы, смотрите по ссылкам, приведенным ниже. Там же можно найти общую информацию о тестировании вообще и о модульном тестировании в частности.

На последок хочется отметить, что разработчики JsTestDriver надеятся, что javascript сообщество объединится в поддержку этого фрэймворка, как единого движка для запуска тестов (наподобие JUnit в Java). Что, совместно с использование различных расширений (например, для YUI Test или QUnit), сделает процесс модульного тестирования javascript максимально продуктивным.

Что ж, кажется JsTestDriver этого вполне достоин.


Ссылки по теме:

2 комментария:

  1. Я когда-то использовал для этих целей JSUnit, он тоже позволяет поднять сервер и запускать тестирование в разных браузерах. Вы не разбирались, в чём отличие?

    ОтветитьУдалить
  2. Я с JSUnit не работал, но глянув документацию могу назвать следующие отличия:

    1. Для JSUnit необходимо написать тестовую HTML-страницу и js-тесты, а для JsTestDriver достаточно только непосредственно javascript тест-кейсов.
    2. При одновременном тестировании на разных машинах(платформах) на каждой из них должен быть запущен отдельный JSUnit-server. Для JsTestDriver тестовый сервер нужен всего один.
    3. Чисто субъективно, JsTestDriver как-то проще и понятнее для меня.

    А уж интеграция JsTestDriver с IDE (Eclipse, IntelliJ IDEA) просто избавила меня от необходимости искать что-то ещё.

    И, да, кстати, не заметил поддержки Opera и Chrome в JSUnit. Мелочь конечно, но всё же...

    ОтветитьУдалить