I created a factory which returns a constructor. The constructor includes a watcher looking at a property of the instance. When that property is changed in tests, the watcher is not fired. Outside of tests, it is fired. $rootScope.$apply doesn't seem to help.
Here is a jsfiddle which contains a simplified version of my code and tests.
http://jsfiddle.net/2Ny8x/234/
//--- CODE --------------------------
(function (angular) {
var myApp = angular.module('myApp', []);
myApp.factory('MyObject', function($rootScope) {
function MyObject() {
this.prop = 5;
this.watcherCallback = angular.noop;
$rootScope.$watch(this.prop, this.watcherCallback);
}
return MyObject;
});
})(angular);
// ---SPECS-------------------------
describe('MyObject', function () {
beforeEach(module('myApp'));
var MyObject, rootScope;
beforeEach(inject(function (_MyObject_, $rootScope) {
MyObject = _MyObject_;
rootScope = $rootScope;
}));
it('calls the watcher when "prop" updates', function() {
var newObj = new MyObject();
spyOn(newObj, 'watcherCallback');
newObj.prop = 10;
rootScope.$apply();
expect(newObj.watcherCallback).toHaveBeenCalled();
});
});
Why doesn't the watcher fire?
var newObj = new MyObject();
This assigns noop
to newObj.watcherCallback
. And it registers newObj.watcherCallback
(so, noop
) as a watcher caallback.
spyOn(newObj, 'watcherCallback');
This replaces newObj.watcherCallback
by another function, which allows you to know when this new spy function is called. But it doesn't register this spy function as a watcher callback to the rootScope. What is register is the original one: noop
.
So, when you change the value of the property and call $apply()
, the rootScope still has a reference to the noop
function and calls it. Your spy is unaware of it.
It should work fine if you do in the constructor:
var that = this;
$rootScope.$watch(this.prop, function() {
that.watcherCallback();
});