the importance of scope.apply() when testing promises

“remember kids, Jasmine likes to apply”

…sorry, what?

Well, basically, it’s simple: when writing unit tests on promises with Jasmine, remember to call $scope.apply(), it will save you some headaches!

take a look at this AngularJs controller. Look at it.

var myApp = angular.module('myApp',[]);
myApp.controller('FooController', ['$scope', 'fooService', function($scope, fooService) {
var instance = this;
$scope.loadingStatus = 'none';
instance.onBarCompleted = function(){
$scope.loadingStatus = 'completed';
};
instance.onBarError = function(){
$scope.loadingStatus = 'error';
};
$scope.callBar = function() {
$scope.loadingStatus = 'loading…';
fooService.bar()
.then(instance.onBarCompleted)
.catch(instance.onBarError);
}
}]);

as you can see, on line 18 there’s a call to fooService.bar() and two callbacks are used to handle the success and error cases.

Here instead, there’s an example of how you could test the error case:

var mockFooService,
sut;
describe('FooController tests', function () {
beforeEach(function($q, $controller){
mockFooService = {
bar: function() {
return $q.reject({ data: { message: 'Error message' } });
}
};
var $scope = {};
sut = $controller('FooController', { $scope: $scope, fooService: mockFooService });
});
it('callBar() should call onBarError() if an error is thrown', function () {
spyOn(sut, 'onBarError').andCallThrough();
scope.callBar();
scope.$apply(); // without the promise will not be evaluated
expect(sut.onBarError).toHaveBeenCalled();
});
});

in the beforeEach() block a mock service is created with a rejected promise (line 9) and on line 15 the controller is instantiated with the mocked dependencies.

On line 23 there’s the core of the test: a call to $scope.apply().
Without it the promise will not be resolved and any method chain will not be executed.
The reason is simple: the promises implementation is tied to the digest cycle, which is not handled by Jasmine. Calling $scope.apply() will update the internal status and take care of digest for you.

Cheers!

the importance of scope.apply() when testing promises
Scroll to top