Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking asynchronous service function using angular/karma/jasmine

The test in this code does not succeed. I can't seem to successfully test the return of an asynchronous function.

describe('mocking services', function () {

    var someService, deferred;

    beforeEach(function () {

        module(function($provide){
            $provide.factory('someService', function($q){
                return{
                    trySynch: function(){
                        return 33;
                    },
                    tryAsynch: function(){
                        deferred = $q.defer();
                        return deferred.promise;
                    }
                };
            });
        });

        inject(function (_someService_) {
            someService = _someService_;
        });
    });

    it('should be able to test values from both functions', function () {
        expect(someService.trySynch()).toEqual(33);

        var retVal;
        someService.tryAsynch().then(function(r){
            retVal = r;
        });
        deferred.resolve(44);
        expect(retVal).toEqual(44);
    });

});

When I run it I get the following error:

Chrome 36.0.1985 (Mac OS X 10.9.4) mocking services should be able to test values from both functions FAILED
        Expected undefined to equal 44.
        Error: Expected undefined to equal 44.
            at null.<anonymous> (/Users/selah/WebstormProjects/macrosim-angular/test/spec/services/usersAndRoles-service-test.js:34:24)

How can I make this test pass?

like image 937
Selah Avatar asked Aug 12 '26 07:08

Selah


1 Answers

When mocking async calls with $q, you need to use $rootScope.$apply() because of how $q is implemented.

Specifically, the .then method does not get called synchronously, it is designed to always be async, regardless of how it was called - sync or async.

To achieve that, $q is integrated with $rootScope. Therefore, in your unit tests, you need to notify the $rootScope that something was changed (ie - trigger a digest cycle). To do that, you call $rootScope.$apply()

See here (specifically the "Differences between Kris Kowal's Q and $q section")

Working code looks like this:

describe('mocking services', function () {

    var someService, deferred, rootScope;

    beforeEach(function () {

        module(function($provide){
            $provide.factory('someService', function($q){
                return{
                    trySynch: function(){
                        return 33;
                    },
                    tryAsynch: function(){
                        deferred = $q.defer();
                        return deferred.promise;
                    }
                };
            });
        });

        inject(function ($injector) {
            someService = $injector.get('someService');
            rootScope = $injector.get('$rootScope');
        });
    });

    it('should be able to test values from both functions', function () {
        expect(someService.trySynch()).toEqual(33);

        var retVal;
        someService.tryAsynch().then(function(r){
            retVal = r;
        });
        deferred.resolve(44);
        rootScope.$apply();
        expect(retVal).toEqual(44);
    });

});
like image 71
Mark Sherretta Avatar answered Aug 13 '26 22:08

Mark Sherretta