Одно дело использовать простую функцию или даже граф объектов в контролируемой среде; другое утверждение — правильно манипулировать DOM: обычно внутри окна набора тестов. Документ пустой и распределяется между тестами.
Например, классический подход к тестированию JavaScript (JsUnit) приводит к страницам, заполненным HTML, которые выполняют множество тестов один за другим и показывают зеленую или красную полосу; есть много возможностей для улучшения.
Шаг 0: макет как можно больше
Тестовые дубликаты существуют и в JavaScript: вам не нужно вызывать проблемные функции, которые манипулируют DOM или создают XMLHttpRequest. Вы можете использовать Test Double двумя способами.
Первый является динамической заменой функций:
var oldCreateElement = {}; myLibrary.createElement = function() { this.called = true; this.arguments = arguments; }; // test code where production code will call myLibrary.createElement... myLibrary.createElement = oldCreateElement;
Второй осуществляется через Впрыск: MyLibrary объект передается в конструктор или к сеттер тестируемого объекта. Это действительно похоже на все другие реализации DI в Java и PHP.
Я советую вам заменять эти приемы только частью вашего кода из-за принципа « Только макеты», который вы используете .
Шаг 1: избегайте отчетов через браузер
Когда среда тестирования использует страницу браузера для составления отчетов, DOM этой страницы будет иметь два применения: предоставлять приспособления для тестов и производственный код для работы, а также предоставлять отчеты конечному пользователю.
Другой подход, за которым следует jsTestDriver , заключается в создании отчетов через консоль: вы выполняете тесты из командной строки или с помощью плагина IDE . Браузер захватывается и используется для выполнения кода, но не показывает результаты: он отправляет их обратно через Ajax на сервер jsTestDriver.
В результате вы можете начать выполнение с любого места и разорвать пустой документ в тесте, не нарушая функциональные возможности платформы.
Шаг 2: избегайте программного создания элементов DOM
Время от времени вам понадобятся некоторые элементы для вашего кода. Даже если вы заглушите код, который работает в DOM, вам все равно придется где-то его тестировать или выполнять функциональные тесты, которые затрагивают большую группу объектов.
Если вы хотите создать HTML-элементы вручную, это то, что вам предназначено :
this.div = document.createElement('div'); var p = document.createElement('p'); div.appendChild(p); p.innerHTML = "bar"; div.id = 'foo';
Скучно читать (и писать), шумно и трудно понять. Может быть, можно сократить с помощью jQuery или других библиотек, но решение jsTestDriver действительно компактно: оно включает в себя вставку HTML-кода в качестве объявления .
Из документации jsTestDriver мы видим один из специальных комментариев, которые интерпретируются для создания элементов HTML:
/*:DOC element = <div><p>foo</p></div>*/ assertNotUndefined(this.element);
Теперь, когда мы знаем, как работают различные шаги, давайте углубимся в код.
пример
Два варианта создания узлов DOM в jsTestDriver:
- создание фрагментов HTML ; корневой элемент хватают и ссылка делается на это для дальнейшего использования.
- добавление HTML к реальному документу , если рабочий код вызывает документ или окно для захвата самих элементов. Конечно, вы должны попытаться ограничить эти вызовы настолько, насколько это возможно, ради простого тестирования в изоляции, но ваши библиотеки могут их вызывать (и jQuery, или ExtJS, будут.)
Вот полный тестовый пример jsTestDriver, который показывает вам функциональные возможности, которые есть в вашем распоряжении.
TestCase('tests that involve the DOM', { 'test that a DOM node created for this test exists' : function () { assertUndefined(this.inputElement); /*:DOC inputElement = <input id="searchbox" value="Type here" /> */ assertNotUndefined(this.inputElement); assertEquals('Type here', this.inputElement.value); }, 'test that a DOM node can be scoped to a single test' : function () { assertUndefined(this.inputElement); }, 'test that a DOM node can be appended to the document' : function () { /*:DOC += <input id="appendedsearchbox" value="Type here" /> */ assertNotNull(document.getElementById('appendedsearchbox')); }, 'test that an appended DOM node is not global anyway' : function () { assertNull(document.getElementById('appendedsearchbox')); }, 'test that HTML code can span over multiple lines' : function() { /*:DOC anotherBox = <input id="anotherbox" value="Some default" /> */ assertEquals('Some default', this.anotherBox.value); }, 'test multiple top-level nodes can be appended' : function() { /*:DOC += <div id="first"></div> <div id="second"></div> */ assertNotNull(document.getElementById('first')); assertNotNull(document.getElementById('second')); } });