Jasmine spies callThrough and callFake

21,345

Solution 1

I have never chained these kinds of functions together cuz in my mind they seem to do the opposite. You are saying when I call this method -onAdmin - in the scope call it as normal. Which is what the callThrough method jasmine provides for us does.

But then you are chaining along a callFake method as well so then you say but dont actually call it call this fake function instead - very conflicting.

If you want to call spy on the method onAdmin and instead of it being fired you want it to do something else - something mocked - then use the .and.callFake(fn). Also take into account like @stefan above said - dont invoke the function - callFake is simply wanting a function as a parameter it will take care of calling it itself.

This might be more along the lines of what you are looking for, if not show us some more code.

spyOn(scope, 'onAdmin')and.callFake(done)

Solution 2

you are calling done right-away when you write done() try passing in done as a value:

spyOn(scope, 'onAdmin').and.callThrough().and.callFake(done)

Solution 3

I found a work around way to do this. Jasmine has a method called addSpyStategy where you can add a custom strategy like callThrough or callFake. It would look something like this:

jasmine.addSpyStrategy('callThroughAndThen', (spy, fn) => {
  return function() {
    spy.and.callThrough();
    setTimeout(() => fn(...arguments), 0);
  }
});

the timeout makes sure the real function finishes before executing the custom function. Then for your spy, you can do this:

const spy = spyOn(scope, 'onAdmin')
 spy.and.callThroughAndThen(spy, () => {
  // your custom callback
  done();
});

note: make sure to put custom strategy in a beforeEach block

Share:
21,345
kkudi
Author by

kkudi

Updated on August 23, 2022

Comments

  • kkudi
    kkudi almost 2 years

    I have a scenario whereby I want to call done() on a beforeEach after a callback has been invoked.

    I tried to do this as follows :

    spyOn(scope, 'onAdmin').and.callThrough().and.callFake(function(){done()})
    

    But I'm not sure I get the right behaviour. Essentially what I want to achieve is to be able to call done() after each callback is done doing what it does.

    UPDATE: workaround solution

    scope.onAdminBackup = scope.onAdmin;
    spyOn(scope, 'onAdmin').and.callFake(function(admin)  {
    
     scope.onAdminBackup();
     done() ;
    
    })  
    
  • kkudi
    kkudi almost 10 years
    Someone edited my question. I had function () { done()} in callFake. Still doesn't work.
  • kkudi
    kkudi almost 10 years
    The only workaround I've found is the following: make a backup of my function before it is spied on. Only callFake with my backup function and then afterwards done. Seems a bit of a hack though
  • kkudi
    kkudi almost 10 years
    I understand that it's actually conflicting. I guess the solution would be to do what I put as my last comment in Stefans answer.
  • stefan
    stefan almost 10 years
    yeah looks cleaner. I would go with the workaround, easier to read too. But why do you need to call the function anyway? do your tests rely on it being called?
  • Sten Muchow
    Sten Muchow almost 10 years
    Interesting test approach! I am curious to see what you are testing with such an approach!? But yes I think thats the only way you are gonna get what you want - and what you want I dont know. Isnt is possible to simple call the original function in the callFake method? As an object is pass-by-reference anyways you are in fact calling the same thing.
  • kkudi
    kkudi almost 10 years
    I actually tried that but it didn't work. It errors with a stack error because it keeps on getting invoked as it's being spied /watched.
  • kkudi
    kkudi almost 10 years
    Yeap they do! Or I want them to be I guess :) those callbacks are some of the ways I know I have data back from the server and some variables are assigned in them and would like to check against them as opposed to faking them.
  • stefan
    stefan almost 10 years
    It's better to test with fake data than having a server running for the tests, you can do e2e testing with protractor to test if the integration with the real app works!
  • stefan
    stefan almost 10 years
    Otherwise you won't know if it's your server or your angular app which is failing a test.