Jasmine spies callThrough and callFake - javascript

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() ;
})

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)

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)

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

Related

Sinon anonymous stub passed as a parameter

I am trying to test a function in a redux container but the issue is barely about redux or react. Basically the fetchData function I am trying to test, takes two functions as parameters and calls them.
What I was hoping to do was to have two anonymous stubs and pass them to the function this way:
var firstStub = sinon.stub().withArgs(mockResponse).returns('success');
var secondStub = sinon.stub().withArgs(mockResponse).returns('success');
AccountApp.fetchData({ firstStub , secondStub });
When this happens my function fetchData complains about the firstStub and secondStub not being a function. I know they are stub objects but if that is the case what is the correct way of managing this situation.
Passing the stubs as
AccountApp.fetchData({ firstStub , secondStub });
seems to be the culprit, because this means that you actually (after ES6 desugaring) invoke this:
AccountApp.fetchData({ firstStub: firstStub, secondStub: secondStub });
and this means that your fetchData() function would need to have an implementation like this:
function(args) {
// ....
args.firstStub(params);
args.secondStub(params);
// ...
};
I seriously doubt that your production code refers to those callbacks as "stubs". So you probably want to invoke the function like this:
AccountApp.fetchData(firstStub, secondStub);

Spy on setTimeout and clearTimeout in Karma and Jasmine

I cannot seem to be able to spy on setTimeout and clearTimeout in my Jasmine tests, which are being run through Karma.
I have tried variations on all of this
spyOn(window, 'setTimeout').and.callFake(()=>{});
spyOn(global, 'setTimeout').and.callFake(()=>{});
spyOn(window, 'clearTimeout').and.callThrough();
clock = jasmine.clock();
clock.install();
spyOn(clock, 'setTimeout').and.callThrough();
runMyCode();
expect(window.setTimeout).toHaveBeenCalled(); // no
expect(global.setTimeout).toHaveBeenCalled(); // nope
expect(window.clearTimeout).toHaveBeenCalled(); // no again
expect(clock.setTimeout).toHaveBeenCalled(); // and no
In every case, I can confirm that setTimeout and clearTimeout have been invoked in runMyCode, but instead I always get Expected spy setTimeout to have been called.
For window, clearly this is because the test and the runner (the Karma window) are in different frames (so why should I expect anything different). But because of this, I can't see any way to confirm that these global functions have been invoked.
I know that I can use jasmine.clock() to confirm that timeout/interval callbacks have been invoked, but it looks like I can't watch setTimeout itself. And confirming that clearTimeout has been called simply isn't possible.
At this point, the only thing I can think of is to add a separate layer of abstraction to wrap setTimeout and clearTimeout or to inject the functions as dependencies, which I've done before, but I think is weird.
I was able to get it to work like this:
spyOn(window, 'setTimeout');
runMyCode();
expect(setTimeout).toHaveBeenCalled();
Just remove the 'window' object from the setTimeout call.
For those looking for a Jest solution, it has dedicated fake timer functions (which are also spyable).
Edit: Since asking this question, it looks like Jasmine has implemented Clock, which makes this kind of mocking possible. And, as Piotr Jaworski's answer points out, Facebook's Jasmine-based Jest provides its own (arguably much better) way of mocking and spying on timed tasks.
So, the rest of the answer is dated....
The only -- and only -- solution I could find for this is to use Rewire (in my case, I am required to also use Rewire-Webpack).
Rewire does allow you to replace global methods -- but once the method has been replaced, it cannot be spied upon. So, to actually to successfully use toHaveBeenCalledWith, you must wrap and proxy the mock function.
var rewire = require('rewire'),
myModule = rewire('./path/to/module');
describe(function () {
var mocks = {
setTimeout: function () { return 99: },
clearTimeout: function () {}
};
beforeEach(function () {
// This will work
myModule.__set__('setTimeout', function () {
mocks.setTimeout.apply(null, arguments)
})
// This will NOT work
myModule.__set__('clearTimeout', mocks.clearTimeout)
});
it('calls setTimeout', function () {
spyOn(mocks, 'setTimeout').and.callThrough();
spyOn(mocks, 'clearTimeout').and.callThrough();
myModule.doSomething(); // this will invoke setTimeout locally
expect(mocks.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 1000);
expect(mocks.clearTimeout).toHaveBeenCalledWith(99); // Won't work (see above)
});
});
Naturally, this will surely stop working the next time Jasmine, Rewire, Karma, Webpack... or the weather... changes (grrr). If this doesn't work for you, please leave a comment so future devs will know.

Calling helper functions from within template.rendered in Meteor.js, error in 1.0, fine in 0.9.3

In an effort to avoid repeating code I found it useful to have helper functions that could be called from within a foo.rendered function (for instance). Why is this possible in 0.9.3 of Meteor, but throws an error in 1.0 ?
Template.foo.helpers({
'fooFn' : function(){
return "something"
}
});
Template.foo.rendered = function(){
var something = Template.foo.fooFn();
}
Should I change the syntax in foo.rendered (am I calling it wrong?) or maybe use a different approach entirely (set up functions outside of the helpers({}) and rendered() and call those? or set this up as a registered helper function?
It looks like it is possible as of Meteor 1.0.3.1 to find and call helper functions, although it is clear it's not supposed to be used like this.
Still it can be done:
Template.foo.__helpers[" fooFn"]()
Please notice the leading space for the function name.
The other way of dealing with this is attaching a function to a global namespace, then calling that from somewhere else in your code, as user3557327 mentioned.
Additionally you can use:
Template.registerHelper('myHelper', function (){return 'Look At Me!'})
to register a global helper, and call it explicitly using:
UI._globalHelpers['myHelper']()
I think this would be a better method: How to use Meteor methods inside of a template helper
Define a function and attach it to the template. Call that function from rendered, as well as your template helper. Like MrMowgli said, you probably aren't "supposed" to call template helpers from within the .js file, only from the ...that could probably break in the future.
For example define a function and attach it to the tamplate:
Template.Play.randomScenario = function () { // HACK HACK HACK }
and then call it from your lifecycle method
Template.Play.created = function () {
Template.Play.randomScenario();
};
scenario: function () {
return Template.Play.randomScenario();;
},
I had the same problem and this is the solution I used. Hope that helps.

Jasmine spyOn: How to make it work when the spyed function reference is passed?

I'm using jasmine to test my javascript, and I'm finding a hard case to spec. I have this function which is being passed directly to the handler:
filters.find('#per_page').change(checkList.filter);
Now, I'd like to spy on it and see if it's being called when i change that:
describe("when I change the number of items per page", function() {
beforeEach(function()
spyOn(checkList, 'filter');
$('#per_page').val('50').trigger('change');
});
it("filters the results list", function() {
expect(checkList.filter).toHaveBeenCalled();
});
});
This test fails. Suprisingly, if I change my code above to:
filters.find('#per_page').change(function() { checkList.filter(); });
it passes. Is there a way to spy on such functions passed or do I have to resort to the anonymous function case?
Yes, there is, but to do so events have to be bound after you have added the spies on your function. The reason for this is that spies replace the property with an other function (it doesn't change the function itself). In your case, what happened is that you assigned the original function and when you added the spies you replaced the property without changing the callback that was bound.
If the way your application is made you can't add your spies before doing the events binding, than the only it can work is to use anonymous function which call the proper callback.

Testing delegate callbacks using Jasmine (JavaScript)

I have a simple JavaScript function that uses two delegates to (asynchronously) get one value back:
function getMyUserName() {
context.load(user);
context.executeQueryAsync(onGetMyUserNameSuccess, onGetMyUserNameFail);
}
function onGetMyUserNameSuccess() {
return user.get_title();
}
function onGetMyUserNameFail(sender, args) {
return args.get_message();
}
The "context" and "user" variables are already set and initialized, and the first delegate ("onGetMyUserNameSuccess") is getting the correct answer. The question is how I can test the "getMyUserName" function with Jasmine?. If I use "runs", I have no way to know the response from the delegates (also I don't see any way to know if the delegates are called). I tried to set spies to mock the delegates, but probably I didn't it correctly (I'm just starting with Jasmine).
Any help will be very welcome.
Thanks in advance,
Gustavo
In most of the cases when you have to work with async code, you should call the function by yourself. But not directly but in the way your code would call it. So in your case spy on context.executeQueryAsync and use spy.mostRecentCall.args to get the reference to the function and then call them.
var async = jasmin.spyOn(context, 'executeQueryAsync');
async.mostRecentCall.args[0]()
var args = {get_message: jasmine.createSpy()}
async.mostRecentCall.args[1]({}, args);
expect(args.get_message.toHaveBeenCalled());
Note that there is the sinon framework that have a bunch of methodes to automatically call callbacks

Categories

Resources