Joshua Ohana
Joshua Ohana

Reputation: 6121

Conditionally resolve or reject $q promise, unit testing with Jasmine

I have a mocked service, personService below, which is being called by the controller personController which is what I'm testing.

I want to test pass/fail paths obviously, so I'm trying to conditionally route the promise resolve/reject based on an input in my test, to test both paths.

    // Mocking personService and its functions
    beforeEach(inject(function($q) {

        personService = {

            addPerson: function (resolve) {
                var deferred = $q.defer();

                if(resolve) {
                    deferred.resolve();
                }
                else {
                    deferred.reject();
                }

                return deferred.promise;
            }
        };

        // And add its spies
        spyOn(personService, 'addPerson').and.callThrough();
    }));

    // Mocking $state
    beforeEach(inject(function () {

        $state = {
            go: function () {
                return true;
            }
        };

        // And its spies
        spyOn($state, 'go').and.callThrough();
    }));

For this particular function, $state.go is called if the service call to addPerson is resolved, and is not called if it's rejected, so I'm laying out my test as below:

        it("on return error from personService.add, state.go not called", function () {
            $scope.addPerson(false);
            $rootScope.$digest();
            expect($state.go).not.toHaveBeenCalled();
        });

        it("on return success from personService.add, state.go called", function () {
            $scope.addPerson(true);
            $rootScope.$digest();
            expect($state.go).toHaveBeenCalled();
        });

In the first test I'm passing false, which should reject the promise, and not call $state.go ... however when the test is actually ran the spy is called on $state.go!

For reference, the controller function in question is below:

var _addPerson = function () {
        personService.addPerson().then(function (response) {
            $state.go('home.person', { personIndex: $scope.personList.length }, { reload: true });
            _gotoLastPerson();
        }),
        function (err) {
            console.log(err);
        };
    };

A perplexing note which indicates I'm close... if I remove the if/then from addPerson and always just deferred.reject() then the first test passes, and the second fails; exact reverse happens if I always deferred.resolve().

1) How to use a conditional $q resolve/reject in a Jasmine mocked service call

Update

After searching around it seems some people put all of this setup within the it() blocks themselves, in which case I could just change the service to explicitly resolve or reject depending on which it() it's in... but that seems like horrible coding practices.

Upvotes: 0

Views: 730

Answers (1)

Joshua Ohana
Joshua Ohana

Reputation: 6121

Hooray! Okay so I don't know why this was happening yet, but the resolve in addPerson: function (resolve) was actually the person object instead of the boolean.

So instead I created a global var resolvePromise which I set to true/false depending on my need and it works beautifully.

Upvotes: 0

Related Questions