3

I’m trying to use Jasmine spies for testing a controller that calls query on a $resource. I can get a successful test when I write my call to the resource as follows (implementation 1 in the plunk linked below)

function($scope, bagelApiService) {
      bagelApiService
        .query()
        .$promise
        .then(function(bagelsResponse) {
          $scope.bagels = bagelsResponse;
          $scope.somethingAfterBagelsLoad = true;
        });
    }

But I would rather call the resource like this (implementation 2 in the plunk linked below)

 function($scope, bagelApiService) {
   bagelApiService.query(function(bagelsResponse) {
       $scope.bagels = bagelsResponse;
       $scope.somethingAfterBagelsLoad = true;
     });
 }

Here is my spec

describe('BreakfastCtrl', function() {
  var $q,
  $rootScope,
  _scope,
  mockBagelsResponse = [{name: 'foobagel'}, {name: 'barbagel'}];

  beforeEach(module('BreakfastApp'));

  beforeEach(inject(function($controller, $q, $rootScope, bagelApiService) {
    _scope = $rootScope.$new();

    var queryDeferred = $q.defer();

    spyOn(bagelApiService, 'query').andReturn({$promise: queryDeferred.promise});

    $controller('BreakfastCtrl', {
      '$scope': _scope,
      'bagelApiService': bagelApiService
    });

    queryDeferred.resolve(mockBagelsResponse);
    $rootScope.$apply();
  }));


  it('should set scope.bagels', function() {
    expect(_scope.bagels).toEqual(mockBagelsResponse);
  });

});

Any idea why implementation 2 fails the test (even though it runs fine), and how the test can be written to pass with implementation 2?

click here for plunk

samseven
  • 33
  • 1
  • 6

1 Answers1

2

It's because you don't mimic the query() completely.

It should be like this (at least to make both of your implementations work).

spyOn(bagelApiService, 'query').andCallFake(function (callback) {
  queryDeferred.promise.then(callback);
  return { $promise: queryDeferred.promise };
});

Example Plunker: http://plnkr.co/edit/wGAytf5ASSJwut4WUwGO?p=preview

runTarm
  • 11,537
  • 1
  • 37
  • 37
  • There is nothing special in the spy api. The `$resource` api support two styles to get the response data. The first is a promise style as your implementation 1. The second is a callback style as your implementation 2. Therefore, if you want to mock the `$resource` api, you have to implement both styles in order to use any of them. – runTarm Aug 11 '14 at 11:44
  • Thanks runTarm. I also found that you can get the callback using "this.get.arguments". In the case where you're passing both input params and a callback to the $resource call, the fake can be implemented as follows: `deferred.promise.then(this.get.arguments[1]); return {$promise: deferred.promise};` – samseven Aug 12 '14 at 11:33