В моем последнем блоге мы рассмотрели основы настройки интеграционного тестирования для директивы Angular, которая использует свойство bindToController
. Как вы помните, мы начали с простой директивы:
и заканчивался следующим блоком кода для нашего тестового файла:
Мы также пришли к выводу, что эта реализация не была особенно гибкой. Примечательно, что это не позволяет нам указывать переменные области видимости для каждого теста.
Так давайте это исправим! Если мы отступим назад и посмотрим на то, что у нас есть на данный момент, мы заметим, что наша функция разделена на две части: одна для упаковки переменных области видимости в строку элемента, а другая — для создания области видимости и контроллера, которые нам понадобятся для тестирования.
Начнем с переменных области видимости.
Рефакторинг значений по умолчанию
Поскольку нам, вероятно, потребуется настроить некоторые переменные области видимости в наших тестах, давайте проясним, что исходные переменные области видимости — это просто значения по умолчанию, переименовав их и переместив из beforeEach
:
Мы убрали конструкцию по умолчанию из beforeEach
по двум причинам: во-первых, она укорачивает функцию beforeEach
и облегчает сосредоточение внимания на ее сути. Во-вторых, он делает значения по умолчанию доступными для всех наших тестов на случай, если они понадобятся для сравнения или как способ более быстрой настройки переменных с похожей структурой. К следующей части!
Упаковка шага компиляции
Решение, позволяющее обеспечить гибкость переменных области видимости, состоит в том, чтобы просто запустить этап компиляции внутри каждого из наших тестов. Поскольку у нас нет доступа к функциям $rootScope
и $compile
за пределами цикла beforeEach
, мы можем обернуть этап компиляции в функцию, назначить ее глобальной области видимости, а затем вызвать функцию из наших тестов:
//The new function. buildDirective = function(){ element = angular.element(defaultElementString); element = $compile(element)(scope); scope.$digest(); controller = element.controller('fancySelect'); } //An example test. Obviously not the most useful of tests. it ("should initialize someVar as true", inject(function(){ buildDirective(); expect (controller.someVar).toBe(true); }))
Что, если у нас есть необязательные атрибуты в нашей директиве? В этом случае мы захотим поддерживать пользовательский elementString
, поскольку мы можем исключить определенные атрибуты директивы в некоторых наших тестах. Достаточно просто:
buildDirective = function(elementString){ if (angular.isUndefined(elementString)) elementString = defaultElementString; element = angular.element(elementString); element = $compile(element)(scope); scope.$digest(); controller = element.controller('searchSelect'); }
Наш последний тестовый файл:
Прежде чем мы закончим, давайте взглянем на несколько разных простых тестовых случаев. Это даст нам лучшее представление о том, как использовать нашу функцию buildDirective()
, а также продемонстрирует некоторые общие тестовые структуры.
Тестирование с нестандартными переменными области видимости:
Просто установите свои собственные переменные области видимости перед вызовом функции buildDirective
, и все готово.
Тестирование исключения:
Единственное примечательное отличие состоит в том, что действие, вызывающее исключение, должно вызываться как часть expect
в сочетании с .toThrowError()
.
Тестирование с имитацией событий:
Для имитации событий мы можем воспользоваться очень удобной функцией .triggerHandler()
. Обратите внимание, что angular поставляется только с jqLite, поэтому вам придется включить полную версию jQuery, если вы хотите использовать .find()
с идентификаторами элементов (или любые другие функции jQuery, не включенные в jqLite).
Вот пример запуска событий «фокус» и «размытие»:
Тестирование с пользовательскими данными о событиях:
Для любого события, требующего дополнительных данных (например, кода клавиши), нам нужно передать объект в функцию .triggerHandler()
:
Обратите внимание, что для того, чтобы это работало, необходима клавиша тип. Оболочка Angular для функции .triggerHandler()
использует наличие ключа type в качестве флага для слияния переданных данных события с фиктивным событием, которое создает .triggerHandler()
. Для полного объяснения этого взаимодействия вы можете перейти по этой ссылке.
Верхушка айсберга
Тестирование — это совсем другая область сложности. То, что вы тестируете, как вы пишете свои тесты и какие типы тестирования вы используете, — все это темы, которые являются предметом обсуждения и в значительной степени зависят от мнения вашей команды и проекта, который вы создаете.
Возможно, я коснусь поверхности этого айсберга в другом блоге, но, по крайней мере, теперь вы можете приступить к тестированию. Даже самый простой подход к тестированию заставит вас задуматься о коде, который вы пишете, и, надеюсь, побудит вас продолжить поиск способов повышения его надежности.
Спасибо за прочтение, надеюсь, вы будете с нетерпением ждать моего следующего блога!