When I try to spyOn the listener function of a $scope.$watch, is like never call the spyOn
http://jsfiddle.net/b8LoLwLb/1/
My controller
angular.module('angularApp')
.controller('MainCtrl', function ($scope) {
$scope.name = '';
this.changeName = function () {
console.log('the name has change to ' + $scope.name);
};
$scope.$watch('name', this.changeName);
});
My test
describe('Controller: MainCtrl', function () {
// load the controller's module
beforeEach(module('angularApp'));
var MainCtrl,
scope;
// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope) {
scope = $rootScope.$new();
MainCtrl = $controller('MainCtrl', {
$scope: scope
});
}));
it('should check if watcher was triggered', function () {
// Spy the listener funtion
spyOn(MainCtrl, 'changeName');
// Change the watched property
scope.name = 'facu';
// Digest to trigger the watcher.
scope.$digest();
// Expect the function to have been called
expect(MainCtrl.changeName).toHaveBeenCalled();
});
});
The problem is that instead of spying the function, the test execute it and print the console log.
I'm using angular 1.4
This is expected behavior, it has nothing to do with jasmine or angular but to do with the function reference held by the property. When you do $scope.$watch('name', this.changeName)
on the controller instantiation, the function reference held by this.changeName
(at that time) is set to be watched. Even if you spyOn the function on the controller instance (later), the function reference held by the property changeName
of controller instance is only changed (to the wrapper function created by jasmine to track the call) but not of the watcher as it still uses the original function reference. So when watch executes it just runs the actual function reference not the spy func reference later you set on changeName
property.
Instead if you do this in your controller:
var vm = this;
$scope.$watch('name', function(){
vm.changeName();
});
You will see your test passing.