“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…';

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,
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.$apply(); // without the promise will not be evaluated

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.