Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I test angularjs directive to spy on the function call?

Code below executes but complains about element.popover not being invoked. I can't seem to figure out what the issue is.

Thanks for help in advance.

directive:

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

Jasmine test:

'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();
    });
  });
});

Output:

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.
like image 404
anazimok Avatar asked Mar 30 '13 15:03

anazimok


1 Answers

Update:

I wasn't able to solve your specific problem. Mostly because I couldn't get angular-seed going/it was taking forever, but I thought I'd make my answer more complete.

There are 2 ways to solve this problem in general:

  1. Spy on a function other than the one being triggered by some event/intermediary
  2. Spy on the prototype of the function before the object is created. In other words: spyOn(MyObjectNamespace.Class.prototype, 'functionToSpyOn')

Afterwards just restore and you should be fine.


I am only vaguely familiar with angular, but have experienced similar problems.

Solution 1

You can just separate out the function rather than specifying it anonymously. This helps test your functionality specifically and avoid all the angular stuff.

Solution 2

Sometimes with frameworks this isn't possible. The main problem here is that your spy is attaching itself too late and the reference is lost or gets overridden.

Test:

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's restore fn instead here
        $.fn.popover = popoverFunction
    });
  });
});

You can use Sinon with Jasmine. Sinon has a spy.restore function that gets rid of the first and last line for you. In my own tests I've placed the first line and the spy creation in a beforeEach and the restore in an afterEach.

like image 192
Parris Avatar answered Sep 27 '22 21:09

Parris