Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing callback from service in an AngularJS controller spec

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();
});
like image 653
lerp90 Avatar asked Aug 12 '26 16:08

lerp90


1 Answers

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.

like image 169
Brocco Avatar answered Aug 15 '26 06:08

Brocco



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!