S. Ravi Kiran
S. Ravi Kiran

Reputation: 4303

Jasmine's spy throwing is not a function error

I have an Angular JS application with a module and some services in it. My controller uses these services. In Jasmine test cases, I created a mock of a service using Jasmine's createSpy. Following is the mocked service:

beforeEach(module(function ($provide) {
    shoppingData = function () {
        getAllItems: jasmine.createSpy('getAllItems');
        addAnItem: jasmine.createSpy('addAnItem');
        removeItem: jasmine.createSpy('removeItem');
   };
   $provide.value('shoppingData', shoppingData);
}));

Controller calls the getAllItems function as soon as an object is created. I created another beforeEach block that creates an object of the controller. Following is the test block to check if getAllItems is called:

it("Should call getAllItems function on creation of controller", function () {
    expect(shoppingData.getAllItems).toHaveBeenCalled();
});

When I run the spec runner page on browser, the test fails with following error: TypeError: 'shoppingData.getAllItems' is not a function

I saw several similar example where this kind of test works without any issue. Can anyone point what is missing or what is going wrong here?

Update: I created a plunker with the part that fails

Upvotes: 3

Views: 25035

Answers (2)

Caio Cunha
Caio Cunha

Reputation: 23394

Seems like a typo if this is the real code. Change the appropriated part to:

shoppingData = {
  getAllItems: jasmine.createSpy('getAllItems'),
  addAnItem: jasmine.createSpy('addAnItem'),
  removeItem: jasmine.createSpy('removeItem')
};

Just changed the function to an object and changed the ; for ,.

UPDATE 1:

Consider only spying at the existing object:

var deferred, _shoppingData;

beforeEach(module('shopping'));

beforeEach(inject(function(shoppingData, $q) {
  _shoppingData = shoppingData;
  deferred = $q.defer();
  spyOn(shoppingData, 'getAllItems').andReturn(deferred.promise);
}));

it('should have called shoppingData.getAllItems', function() {
  expect(_shoppingData.getAllItems).toHaveBeenCalled();
});

Upvotes: 3

Destron
Destron

Reputation: 404

Why don't you try:

var scope, myController;

beforeEach(inject(function($rootScope, $controller) {
  scope = $rootScope.$new();
  myController = $controller.('controllerName', {
    $scope: scope,
    shoppingData: jasmine.createSpyObj('shoppingData', ['getAllItems', 'addAnItem', 'removeItem'])
  });
}));

Upvotes: 0

Related Questions