94

AngularJS v1.2.26

Jasmine v2.2.0

How can I change or remove the behavior of a spyOn? When I try to override it, I get the following error: Error: getUpdate has already been spied upon

var data1 = 'foo';
var data2 = 'bar';

describe("a spec with a spy", function(){

    beforeEach(module('app'));

    var $q;

    beforeEach(inject(function(_updateService_, _$q_){
        updateService = _updateService_;

        //spy the results of the getUpdate()
        $q = _$q_;
        var deferred = $q.defer();
        deferred.resolve( data1 );
        spyOn(updateService, 'getUpdate').and.returnValue(deferred.promise);

    }));

    describe('and here the spy should be different', function() {

        it('returns a different value', function() {

          var deferred = $q.defer();
          deferred.resolve( data2 );
          spyOn(updateService, 'getUpdate'); //ERROR HERE
          updateService.getUpdate.and.returnValue(deferred.promise);

          ...

        });
    });

...

When I remove the second spyOn the test doesn't work.

How do I do this?

emersonthis
  • 32,822
  • 59
  • 210
  • 375
  • 1
    Possible duplicate of [Jasmine SpyOn same method more than once](https://stackoverflow.com/questions/36200532/jasmine-spyon-same-method-more-than-once) – Bohdan Lyzanets Oct 07 '19 at 13:58

6 Answers6

118

You can just overwrite it

updateService.getUpdate = jasmine.createSpy().and.returnValue(etc)
Peter Ashwell
  • 4,292
  • 2
  • 18
  • 22
  • 4
    Is there way to remove the spy entirely? To go back to the original function? – emersonthis Mar 04 '15 at 19:52
  • 4
    The big question is, if you don't the same functionality in every test, why have a global spy at all? If you want to set the spy for every test, then set the spy for every test. – Jan Jul 06 '15 at 21:07
  • 21
    @Jan If I have fifty tests, and only one of them has a spy for a function that is different from the rest, I'd rather just change it the one time there instead of every single test. – theblang May 18 '16 at 19:57
  • 1
    well, in my case it's on a global object (defined by native code). So I have multiple specs that need to return different values in different situations.... – FlavorScape May 05 '17 at 04:20
  • 1
    would it return to its original implementation by calling jasmine.createSpy().and.callThrought()? – aj go Oct 19 '20 at 12:46
37

You can override the return value of the spy

    var deferred = $q.defer();
    deferred.resolve( data1 );

    var getUpdateSpy = spyOn(updateService, 'getUpdate').and.returnValue(deferred.promise);



    var newDeferred = $q.defer();
    newDeferred.resolve( data2 );

    getUpdateSpy.and.returnValue(newDeferred.promise);        
35

Since jasmine v2.5, use the global allowRespy() setting.

jasmine.getEnv().allowRespy(true);

You'll be able to call spyOn() multiple times, when you don't want and/or have access to the first spy. Beware it will return the previous spy, if any is already active.

spyOn(updateService, 'getUpdate').and.returnValue(deferred.promise);
...
spyOn(updateService, 'getUpdate').and.returnValue(deferred.promise);
André Werlang
  • 5,839
  • 1
  • 35
  • 49
  • Honestly, this is the answer I've been looking for ages. I wish it was more prevalent on the internet. A million thanks :) – Novastorm Jul 23 '19 at 09:39
14

More easier and simpler way:

updateService.getUpdate.and.returnValue(Observable.of({status:true}));

It will return the value.

Aniruddha Das
  • 20,520
  • 23
  • 96
  • 132
10

Another option:

(yourService.method as jasmine.Spy).and.returnValue(value);
Justinas Jakavonis
  • 8,220
  • 10
  • 69
  • 114
4

the green check-marked answer didn't work for me, but this did:

yourCoolService.createThing = jasmine.createSpy('notreal', function(){}).and.returnValue();

your jasmine test will run but when you go to fire up your app typescript will yell loudly at you if you don't put a random string and an empty function as the args to createSpy().

shilovk
  • 11,718
  • 17
  • 75
  • 74
rb1econ
  • 39
  • 1