3

I've got a Controller that uses a service that has a callback function on success.

Controller Function

itemCtrl.save = function () {
    ItemService.save({ username: SessionService.getUsername() }, itemCtrl.item, function (res) {
        $scope.$emit('UPDATE_ITEMS');
        $modalInstance.close(res);
    });
};

spec

it('should close the modal on successful save', function () {
    spyOn(itemSrv, 'save').and.callFake(function () {
        deferred = q.defer();
        deferred.resolve();
        return deferred.promise;
    });
    spyOn(modalInst, 'close').and.callThrough();
    ItemCtrl.save();
    scope.$digest();
    expect(modalInst.close).toHaveBeenCalled();
});

When I run this test in karma is failing because the close function of the modalinstance is not called. I think the problem is on the spec description, because the functionality is working in the app. I have a few questions about this:

  • Is this the best way of describing callbacks on service calls in a controller?
  • How should I describe my specs using callbacks?

-- EDIT --

I've changed my service calls to make them handle results as promises:

itemCtrl.save = function () {
    ItemService.save({ username: SessionService.getUsername() }, horas.trabajo).$promise.then(function (res) {
        $scope.$emit('UPDATE_ITEMS');
        $modalInstance.close(res);
    });
};

I guess I'm not passing the tests because my service does not return a promise (it has a promise attribute which I'm calling to emit the event and close the modalInstance). But still I'm getting confused about how to return a promise using ng-resource.

My attempt to solve it was returning an object with a $promise attribute, which contains the promise that q provides.

spec

it('should close the modal on successful save', function () {
    spyOn(itemSrv, 'save').and.callFake(function () {
        deferred = q.defer();
        deferred.resolve();
        return {$promise: deferred.promise};
    });
    spyOn(modalInst, 'close').and.callThrough();
    ItemCtrl.save();
    scope.$digest();
    expect(modalInst.close).toHaveBeenCalled();
});

1 Answer 1

3

You are confusing the concepts of promises and callbacks, but to get it to work as you currently have it you can use this:

spyOn(itemSrv, 'save').and.callFake(function (params, callback) {
    callback({some: 'response'});
});

Callback

You pass in a function that will be called based upon a scenario

Promise

Get back an object which contains a method on it (then) which will call either the 1st param on success or the 2nd param on error

There is more to both, but this is a general overview.

Also, I'd suggest changing the save method in your service to return a promise rather than accepting a callback.

Sign up to request clarification or add additional context in comments.

3 Comments

if I change it and return a promise I should be able to test like I did in the question, right?
Yes, the testing code you have there should be the same
I've added my attempt of solving it at the bottom of the first post, I guess I'm getting a bit confused with promises and ng-resource.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.