Whenever you make an asynchronous remote call there is always the chance that the call will fail. In this post I will demonstrate how to simulate and test failing promises when using Angular and $q.

Whenever you make an asynchronous remote call there is always the chance that the call will fail. In this post I will demonstrate how to simulate and test failing promises when using Angular and $q.

The following code shows a controller with a call to a greeting service and some basic error handling.

angular.module('app').controller('greetingController',
    ['$scope',
    '$q',
    'greetingService', function($scope,$q,greetingService){
        greetingService.greetPerson(4).then(function(greeting){
            $scope.greeting = greeting;
        }).catch(function(error){
            $scope.friendlyErrorMessage = 'We are sorry, please try again later';
        });
    }]
);

The goal of this simple exercise is to successfully simulate and test an error condition that causes the catch block of the promise to execute. It turns out that the setup of the test makes it almost trivial to hijack the promise and inject our own mock behavior.

Below I have created a simple Jasmine test where I'm able to successfully simulate the required error condition.

describe('GreetingControllerTest', function(){
    var $scope;
    var $q;
    var $controller;

    beforeEach(function(){
        module('app');
        inject(function ($injector) {greetingService = $injector.get('greetingService');
            $scope = $injector.get('$rootScope').$new();
            $q = $injector.get('$q');
            $controller = $injector.get('$controller');
        });
    });

    it('should create friendly error message in case of failure', function(){
        
        var rejectedPromise = function(){
            var p = $q.defer();
            p.reject('error');
            return p.promise;
        };

        var greetingServiceMock = {greetPerson : rejectedPromise};
        $controller('greetingController', {'$scope':$scope,'greetingService':greetingServiceMock });
        $scope.$digest();
        
        expect($scope.friendlyErrorMessage).toBe('We are sorry, please try again later');
    });
});

The key part is the rejectPromise method that we can pass in as part of the mock service injected into the controller. As you can tell, passing dependencies to controllers is trivial if we make use of the $controller function to instantiate the greetingController.