Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

How to simulate and test failing promises in Angular

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Sun May 10 2015

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.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter