Как проверить директиву angularjs, чтобы шпионить за вызовом функции?

Код ниже выполняется, но жалуется, что element.popover не будет вызван. Я не могу понять, в чем проблема.

Спасибо за помощь заранее.

директива:

angular.module('directives', []).

directive('popOver', function ($http) {

    return {
        restrict:'C',

        link: function (scope, element, attr) {
            element.bind('mouseover', function (e) {
                $http.get("someurl" + attr.chatid + ".json").success(function (data) {
                    element.popover({content: data.firstName + " " + data.lastName });
                });
            });
        }
    }
})

Тест на жасмин:

'user strict'

describe('directives', function() {
    beforeEach(module('directives'));
    describe('popOver', function() {
    var $scope, compile, location,  $httpBackend, elm;

    beforeEach(inject(function($rootScope, $compile, _$httpBackend_) {
        $scope = $rootScope.$new();
        compile = $compile;
        $httpBackend = _$httpBackend_;
        elm = angular.element('<i class="pop-over" data-placement="top" data-chatid="testChatId" > </i>');
        compile(elm)($scope);

    }));

    it('should call element.popover()', function() {
        $httpBackend.expectGET('someurl/testChatId.json').
            respond([ {firstName: 'test', lastName: 'user'}]);

        spyOn(elm, 'popover').andCallThrough();

        elm.trigger('mouseover');
        $httpBackend.flush();

        expect(elm.popover).toHaveBeenCalled();
    });
  });
});

Вывод:

Chrome 26.0 (Mac) directives popOver should call element.popover() FAILED
Expected spy popover to have been called.
Error: Expected spy popover to have been called.

Ответ 1

Обновление:

Я не смог решить вашу конкретную проблему. В основном потому, что я не мог получить angular -seed going/it take take forever, но я думал, что сделаю свой ответ более полным.

Есть два способа решить эту проблему в целом:

  • Шпион по функции, отличной от той, которая вызвана некоторыми Событие/посредник
  • Шпион на прототипе функции до создания объекта. Другими словами: spyOn(MyObjectNamespace.Class.prototype, 'functionToSpyOn')

После этого просто восстановите, и все будет хорошо.


Я только смутно знаком с angular, но испытал схожие проблемы.

Решение 1

Вы можете просто выделить функцию, а не указывать ее анонимно. Это помогает точно протестировать вашу функциональность и избежать всех angular.

Решение 2

Иногда с фреймворками это невозможно. Основная проблема заключается в том, что ваш шпион слишком поздно присоединяется к себе, и ссылка теряется или становится переопределенной.

Тест:

describe('directives', function() {
    beforeEach(module('directives'));
    describe('popOver', function() {
    var $scope, compile, location,  $httpBackend, elm;

    beforeEach(inject(function($rootScope, $compile, _$httpBackend_) {
        $scope = $rootScope.$new();
        compile = $compile;
        $httpBackend = _$httpBackend_;
        elm = angular.element('<i class="pop-over" data-placement="top" data-chatid="testChatId" > </i>');
        compile(elm)($scope);

    }));

    it('should call element.popover()', function() {
        var popoverFunction = $.fn.popover;
        $httpBackend.expectGET('someurl/testChatId.json').
            respond([ {firstName: 'test', lastName: 'user'}]);

        spyOn($.fn, 'popover').andCallThrough();

        elm.trigger('mouseover');
        $httpBackend.flush();

        expect($.fn.popover).toHaveBeenCalled();
        //restore popover, use sinon restore fn instead here
        $.fn.popover = popoverFunction
    });
  });
});

Вы можете использовать Синон с Жасмином. У Sinon есть функция spy.restore, которая избавляется от первой и последней строки для вас. В моих собственных тестах я поместил первую строку и создание шпиона в beforeEach и восстановление в afterEach.

Ответ 2

Я получил его на работу.
jquery и jquery popover js файлы должны быть загружены до angular.js во время теста. Этот порядок должен быть указан в файле testacular.conf.js. Кроме того, отсутствовал URL-адрес для http '/. Вот код, который работает для меня:


angular.module('directives', []).

directive('popOver', function($http) {

  return {
    restrict: 'C',

    link: function(scope, element, attr) {
      element.bind('mouseover', function(e) {
        $http.get("someurl/" + attr.chatid + ".json").success(function(data) {
          element.popover({
            content: data.firstName + " " + data.lastName
          });
        });
      });
    }
  }
})



'user strict'

describe('directives', function() {
  beforeEach(module('directives'));
  describe('popOver', function() {
    var $scope, compile, location, $httpBackend, elm;

    beforeEach(inject(function($rootScope, $compile, _$httpBackend_) {
      $scope = $rootScope.$new();
      compile = $compile;
      $httpBackend = _$httpBackend_;
      elm = angular.element('<i class="pop-over" data-placement="top" data-chatid="testChatId" > </i>');
      compile(elm)($scope);

    }));

    it('should call element.popover()', function() {
      $httpBackend.expectGET('someurl/testChatId.json').
      respond([{
        firstName: 'test',
        lastName: 'user'
      }]);
      //spyOn(elm, 'popover').andCallThrough();
      spyOn($.fn, 'popover').andCallThrough();

      elm.trigger('mouseover');
      $httpBackend.flush();

      //expect(elm.popover).toHaveBeenCalled();
      expect($.fn.popover).toHaveBeenCalled();
    });
  });
});