Search code examples
javascriptangularjsunit-testingjasmineangularjs-ngmock

How do you use unit test a factory using $provide and $q


I'm trying to write a unit test for a service that has a dependency on another factory function that returns a promise. I've followed the process as seen in this question and answer, but for some reason the code in the then() that I'm trying to verify isn't being called. How do I get this to work? I'd like to use the version that is not doing the spyOn if possible (the Update section in the answer).

var app = angular.module('test', []);

app.factory('managerService', (managerModel) => {
    var service = {
        getCurrentUser: getCurrentUser
    }

    function getCurrentUser() {
        return managerModel.api.get();
    }
    return service;
})

describe('promise with provider', () => {
    var managerModelMOCK = {
        api: {}
    };
    var $q;
    var $rootScope;
    var deferredUser;
    //   beforeEach(inject(()=> {})) //no inject here: Injector already created, can not register a module!

    beforeEach(angular.mock.module('test'));

    beforeEach(() => {
        angular.mock.module(function ($provide) {
            $provide.value('managerModel', managerModelMOCK); // override version here
        });
    });

    beforeEach(inject(($q) => {
        deferredUser = $q.defer();
        managerModelMOCK.api.get = () => {
            return deferredUser.promise;
        }
    }));


    it('should resolve a promise', angular.mock.inject((managerService, $rootScope) => {
        expect(managerService).toBeDefined();
        var expected = {
            Name: 'Somebody'
        };
        var actual;

        //this doesn't work either
        // var innerPromise = $q.defer();
        // innerPromise.resolve(expected);

        // managerModel.api.get = () => innerPromise.promise;

        deferredUser.resolve(expected);

        $rootScope.$apply();

        managerService.getCurrentUser()
            .then((user) => {
                actual = user;
            });

        //undefined, why?
        expect(actual).toBeDefined();
    }))
})

Solution

  • Ugh, promises rookie mistake. the then() functions in a promise chain need to return, so this code works:

        managerService.getCurrentUser()
            .then((user) => {
                actual = user;
                //promise chains have to return
                return;
            });
    
        //rootscope.$apply() had to be moved
        $rootScope.$apply();
    

    I had tried putting the `$rootScope.$apply() after the promise chain before, but of course not having the return in there still kept that from functioning.