$timeout, запускающий инициализацию приложения в тесте директивы angular и макет ошибки $ httpBackend

#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;
    });

});