#angularjs #unit-testing #angularjs-directive
#angularjs #модульное тестирование #angularjs-директива
Вопрос:
У меня проблема с тестом (Karma Mocha Chai). Я тестирую довольно простую директиву, являющуюся частью более крупного модуля angular (webapp). Проблема в том, что при вызове $timeout.flush()
в моем тесте модуль / приложение get инициализируется и отправляет запрос на получение шаблона для домашней страницы. Поскольку $httpBackend
(часть ng-mock) не ожидает никакого запроса, он завершается ошибкой:
Unexpected request: GET /partials/homepage
No more request expected
$httpBackend@/Users/doup/Sites/projects/visitaste-web/bower_components/angular-mocks/angular-mocks.js:1208:1
...
continues
Как возможно, что директива запускает инициализацию модуля??Есть идеи, как избежать этой проблемы? Предпочтительно без вырезания этого кода в другой модуль.
Спасибо!
Здесь директива:
module.exports = ['$timeout', function ($timeout) {
return {
restrict: 'A', // only for attribute names
link: function ($scope, element, attrs) {
$scope.$on('vtFocus', function (event, id) {
if (id === attrs.vtFocus) {
$timeout(function () {
element.focus();
}, 0, false);
}
});
}
};
}];
И вот фактический тест:
describe('vtFocus', function() {
var $scope, $timeout, element;
beforeEach(module('visitaste'));
beforeEach(inject(function ($injector, $compile, $rootScope) {
$scope = $rootScope.$new();
$timeout = $injector.get('$timeout');
element = angular.element('<input vt-focus="my-focus-id"/>');
$compile(element)($scope);
}));
it('should focus the element when a vtFocus event is broadcasted with the correct focus ID', function () {
expect(element.is(':focus')).to.be.false;
$scope.$broadcast('vtFocus', 'my-focus-id');
$timeout.flush();
expect(element.is(':focus')).to.be.true;
});
it('should NOT focus the element when a vtFocus event is broadcasted with a different focus ID', function () {
expect(element.is(':focus')).to.be.false;
$scope.$broadcast('vtFocus', 'wrong-id');
$timeout.flush();
expect(element.is(':focus')).to.be.false;
});
});
Это та часть, где я настраиваю пользовательский интерфейс-маршрутизатор для пути /
в app.config()
:
// ...
$stateProvider
.state('homepage', {
url: '/',
templateUrl: '/partials/homepage',
});
// ...
Комментарии:
1. Где код, который выполняет запрос на
/partials/homepage
? Это в одном из ваших контроллеров? Вероятно, это$scope.broadcast()
то, что запускает дайджест в вашем контроллере. Но вопрос в том, почему ваш контроллер создается здесь? У меня возникла эта проблема при тестировании контроллера (не директивы). В этом сценарии имеет смысл, почему генерируется запрос, и поэтому я просто ожидаю этот запрос в тесте.2. Я не создаю экземпляр какого-либо контроллера ни в тесте, ни в директиве, я говорю о директиве здесь.
/partials/homepage
На него нет прямой ссылки, я полагаю, что он вызывается UI-Router при инициализации path/
. Чего я не понимаю, так это почему мой$broadcast
запускает инициализацию приложения / пользовательского интерфейса-маршрутизатора, событие должно перейти от родительского к дочернему (следовательноbroadcast
) и$scope
в тесте это не связано ни с какой другой областью.3. В любом случае, тестируемый код совершенно неуместен, я знаю, что он работает, и это не критично, но я хочу знать, почему это происходит, поскольку это произошло в другом тесте, проблему которого, к сожалению, я смог обойти. Но это сигнал о том, что что-то не в порядке. Я делаю что-то не так. 🙁
4. Да, я знаю, что вы не создаете экземпляр контроллера в тесте. Я спрашиваю, в какой части вашего кода вызывается шаблон
/partials/homepage
? Это может дать нам ключ к пониманию того, почему выполняется запрос.5. @sunil-d У меня есть маршрут, определенный через UI-Router , для
/
которого загружается/partials/homepage
шаблон (вconfig()
), но я не связал ни одного контроллера для маршрута. Это похоже на$timeout
загрузку приложения или что-то в этом роде… но только при его вызове, не раньше (!!!). Ооо, это сбивает с толку. ^_^
Ответ №1:
В качестве обходного пути я просто переместил директивы в его собственный модуль visitaste.directives
и загрузил этот модуль в тест, так что теперь он отделен от UI-Router и не запускает запрос к шаблону.
Тем не менее, я подожду другого решения, прежде чем приму этот ответ.
describe('vtFocus', function() {
var $scope, $timeout, element;
beforeEach(module('visitaste.directives'));
beforeEach(inject(function ($compile, $rootScope, _$timeout_) {
$scope = $rootScope.$new();
$timeout = _$timeout_;
element = angular.element('<input vt-focus="my-focus-id"/>');
element.appendTo(document.body);
$compile(element)($scope);
}));
afterEach(function () {
element.remove();
});
it('should focus the element when a vtFocus event is broadcasted with the correct focus ID', function () {
expect(document.activeElement === element[0]).to.be.false;
$scope.$broadcast('vtFocus', 'my-focus-id');
$timeout.flush();
expect(document.activeElement === element[0]).to.be.true;
});
it('should NOT focus the element when a vtFocus event is broadcasted with a different focus ID', function () {
expect(document.activeElement === element[0]).to.be.false;
$scope.$broadcast('vtFocus', 'wrong-id');
expect(document.activeElement === element[0]).to.be.false;
});
});