I want to spy on a function, then execute a callback upon function completion/initial call.
The following is a bit simplistic, but shows what I need to accomplish:
//send a spy to report on the soviet.GoldenEye method function
var james_bond = sinon.spy(soviet, "GoldenEye");
//tell M about the superWeapon getting fired via satellite phone
james_bond.callAfterExecution({
console.log("The function got called! Evacuate London!");
console.log(test.args);
});
Is it possible to do this in Sinon? Alternate libraries welcome as well if they solve my problem :)
It's clunky but you can:
//send a spy to report on the soviet.GoldenEye method function
var originalGoldenEye = soviet.GoldenEye;
var james_bond = sinon.stub(soviet, "GoldenEye", function () {
var result = originalGoldenEye.apply(soviet, arguments);
//tell M about the superWeapon getting fired via satellite phone
console.log("The function got called! Evacuate London!");
console.log(arguments);
return result;
});
You have to stub the function. From the docs:
stub.callsArg(index);
Causes the stub to call the argument at the
provided index as a callback function. stub.callsArg(0); causes the
stub to call the first argument as a callback.
var a = {
b: function (callback){
callback();
console.log('test')
}
}
sinon.stub(a, 'b').callsArg(0)
var callback = sinon.spy()
a.b(callback)
expect(callback).toHaveBeenCalled()
//note that nothing was logged into the console, as the function was stubbed
Related
My context is as follows:
I have a variable 'qq' - I want to assign data to this variable from an API call, and once the data has been assigned, I then use this data in a paint/binding process.
The API call and assigning to var 'qq' works fine - but I cant seem to get my callback to work - I keep getting an error: callback() is not a function
This is what my code currently looks like:
var qq = [];
// binding function - to be run after data loader has completed
function sayIamDone() {
alert('I Am Done');
// do data bindings with qq
}
// load data from api into var qq
function dataLoader(inp,callback) {
let url = 'http://localhost:65232/api/layercolor/' + inp;
console.log(url);
$.getJSON(url,function(result) {
qq = eval('({' + result + '})');
//console.log(qq);
callback();
});
}
// call data loader
dataLoader('Prov', sayIamDone());
From the above I get the following error in my chrome console:
Uncaught TypeError: callback is not a function
I have tried the following - which seems to work:
dataLoader('Prov', function () { alert('I Am Done');});
but it is not ideal in my context, as I want to call dataLoader('Prov',XXX()) where XXX() could be a number of different functions using the newly loaded values in qq
Any suggestions as to where I am missing the boat please?
By running dataLoader('Prov', sayIamDone()); you are passing to dataLoader the result of sayIamDone function. You have to pass it without parenthesis in order to pass the function itself.
dataLoader('Prov', sayIamDone);
change:
dataLoader('Prov', sayIamDone());
to
dataLoader('Prov', sayIamDone);
because:
sayIamDone is function ,sayIamDone() is the result of function.Normally, sayIamDone() is undefined or return value in the sayIamDone;
want to use arguments?you can do this:
dataLoader('Prov', callFunc(n));
function callFunc(arg) {
return function() {
sayIamDone(arg);
};
}
or more arguments?you can do this by spread
dataLoader('Prov', callFunc(n));
function callFunc(...arg) {
return function() {
sayIamDone.apply(null, arg);
};
}
I set up a callback function inside my Meteor async method to be called on "readable" event. But the callback is not being called when the on."readable" is being fired (I know it's being fired from the console.log I set up).
Am I missing something here? I've been at it for a few hours now trying a few different things!
Meteor.startup(() => {
Meteor.call("getfeed", function(feedloader) {
//I get: TypeError: undefined is not a function]
console.log(feedloader);
});
});
Meteor.methods({
getfeed: function(callb) {
var req = request('http://feeds.feedburner.com/Techcrunch');
var feedparser = new FeedParser();
testing = [];
//........a bunch of functions........
feedparser.on('readable', function() {
var stream = this
, meta = this.meta
, item;
while (item = stream.read())
{
//I'm pushing the results into testing var
testing.push(item);
}
//From the logs I can see that this is called 12 times
//but the callback's not firing!!!
console.log(testing.length);
callb(testing);
});
}
});
Meteor methods are not asynchronous functions in the sense that they do not get the callback argument even though you pass it when you "call" a method. Instead each method is executed within a Fiber which is another flavor of dealing with asynchronous code.
Fortunately, Meteor has a nice helper that allows you to mix both styles. What you need to do is wrap the "pure" asynchronous part of your method code with Meteor.wrapAsync. This structure should look more or less like this:
Meteor.methods({
getfeed: function() {
var wrapped = Meteor.wrapAsync(function (callb) {
var feedparser = new FeedParser();
testing = [];
// ...
feedparser.on('readable', function() {
// probably the same code you have, but without "callb()"
});
feedparser.on('end', function () {
// NOTE: No error here, so the first argument must be null.
callb(null, testing);
})
});
// NOTE: Finally, call the wrapped function
return wrapped();
}
});
I´ve been making a prototype of webservice and I get confronted with the following problem: Whenever I try to use a 'var' as a callback i get: undefined.
What I'm tryng to do is:
var mysqlquery = function (){
var vAlue = XXX;
}
var service = function (pp, ee, cb){
var toReturn= ({ //XML code
Value: vAlue
})
cb(toReturn);
};
Output should be XXX
The service runs fine and logs the values, but when i try to make it a callback to respond it is undefined, I guess because of the async of node.
Already tried making it a global or global.window or calling it inside another function, none of wich work. I don´t want to use extra modules for this, is there any method for it ? (Also tried this.)
Any tip is much apreciated, thanks.
You already know that defining a var in a function limits its scope to that function, but you can pass that data out of the function using a return, like so:
var mysqlquery = function (){
return 'XXX';
}
console.log(mysqlquery())
> "XXX"
What's happening in the console.log line is that your function is being evaluated, and returns "XXX", and then it is passed to console.log.
This is the foundation of callbacks: if you have an asynchronous function, you can pass a callback function into it to feed in the result of the async function:
function print(res) {
console.log(res)
}
function asyncThing(cb) {
var ten = 5 + 5
window.setTimeout(cb.bind(this, ten), 5000)
}
asyncThing(print)
... [wait five seconds]...
> 10
This question already has answers here:
AngularJS : Where to use promises?
(4 answers)
Closed 7 years ago.
I have the following code:
//calling AsyncFunction
var coords = LocationSerivice.getLocation();
var localStores;
setTimeout(function(){
//works
console.log(coords);
//calling async function again
localStores = LocalStoresService.getLocalStores(coords[0],coords[1]);
}, 100);
setTimeout(function(){
//doesn't work
console.log(localStores);
}, 100);
What am I trying to do is to first call a Async function that returns my location. Then in order to get the data from that function I am setting timeout. With the data received I am calling second Async function. And again I am setting timeout in order to get the data from my second Async function, so here it fails. If I try to display the data with console.log() in the timeout block it says undefined while console.log()in the first timeout block works.
I have tried also increasing the timeout time but it just doesn't work.
Any suggestions how to get over that ?
You could modify your LocalStoresService.getLocalStores method to accept a callback.
function getLocalStores (arg1, arg2, cb) {
// do stuff.
var localStores = 'w/e';
if (typeof cb === 'function') return cb(localStores);
}
So your invoking method would look like this:
var coords = LocationService.getLocation();
LocalStoresService.getLocalStores(coords[0], coords[1], function (localStores) {
console.log(localStores); // 'w/e'
});
It really depends on the type of technology you're using, as the "correct" implementation of async methods can vary from type to type (AngularJS, NodeJS, vanilla JS etc..)
As i understand your project might be using angularjs i am giving example based on that ,
.factory('LocationSerivice', function ($http) {
return{
getLocation : function () {
return $http.get('location_api');
};
getLocalStores : function(coords){
return $http.get('your_local_store_api');
};
}
});
Now call your API's
LocationService.getLocation().success(function(coords){
LocationService.getLocalStores(coords).success(function(localStores){
console.log(localStores);
});
});
In this contrived case, I have a service that takes one dependency. The service exposes one method that needs to call through to the dependency.
If I wrap this method call in its own function, then spyOn works as expected (MyService1). However, if I delegate to this service directly, then spyOn fails (MyService2).
My questions are:
Can someone please explain this behaviour?
If I want to use spyOn, must I re-write all my directly delegated methods as wrappers, or is there a way around this?
Thanks.
describe('spyOn problem', function() {
// wraps dependency function - can spy on
var MyService1 = function (dependency) {
this.continue = function() {
dependency.nextStage();
};
};
// delegates to dependecy function - cannot spy on
var MyService2 = function (dependency) {
this.continue = dependency.nextStage;
};
var dependencyMock = {
nextStage: function () {
}
};
it('should call nextStage method of MyService dependency', function () {
var service = new MyService1(dependencyMock);
spyOn(dependencyMock, 'nextStage'); // also tried with .andCallThrough()
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Fail: "Expected spy nextStage to have been called"
});
it('should call nextStage method of MyService2 dependency', function () {
var service = new MyService2(dependencyMock);
spyOn(dependencyMock, 'nextStage');
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Pass
});
});
Jasmine Spies works on the objects, which means for spyOn(dependencyMock, 'nextStage'), the function nextStage on the object dependencyMock will be replaced with a jasmine spy function with this statement.
In MyService2 test case, the spy is installed after actually assigning the function nextStage to continue, which means continue will refer to the actual function nextStage and not to spy. By modifying the test case as below, the spy will be assigned instead.
it('should call nextStage method of MyService2 dependency', function () {
spyOn(dependencyMock, 'nextStage');
var service = new MyService2(dependencyMock);
service.continue();
expect(dependencyMock.nextStage).toHaveBeenCalled();
// Pass
});
In the MyService1 test case, even though you are calling service.continue(), it internally operates on 'dependencyMask` object for which spy is installed.