How do I test angularjs directive to spy on the function call?
angularjs, jasmine, javascript, unit-testing
Solution
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:
- Spy on a function other than the one being triggered by some event/intermediary
- 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.
Problem
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. ```