Jasmine spies callThrough and callFake
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
kkudi
Updated on August 23, 2022Comments
-
kkudi almost 2 years
I have a scenario whereby I want to call
done()
on abeforeEach
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 almost 10 yearsSomeone edited my question. I had function () { done()} in callFake. Still doesn't work.
-
kkudi almost 10 yearsThe 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 almost 10 yearsI 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 almost 10 yearsyeah 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 almost 10 yearsInteresting 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 almost 10 yearsI 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 almost 10 yearsYeap 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 almost 10 yearsIt'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 almost 10 yearsOtherwise you won't know if it's your server or your angular app which is failing a test.