Mocha testing a bluebird powered node style callback - javascript

I am facing trouble to get pass a test by running mocha which seems to be passing.
The test:
describe('.get()',function() {
it('should be called once',function() {
// => Need to spy on this
var callback = function(err,data) {
console.log('I am called');
if (err) {
console.log('I am logging the error '+err);
} else {
console.log('I am logging the data '+data);
}
}
agentMock._response = {body:'something',headers:'headers'};
// => Start Spying
var spy = sinon.spy(callback);
sinon.spy(agentMock,'get');
baseRequest.get(spy); // refer (a) below
expect(agentMock.get).to.have.been.calledOnce;
expect(spy).to.have.been.calledOnce;
expect(spy).to.have.been.calledWith(null,'data');
});
});
I want to test whether the callback is called or not. Therefore, I logged in the body of the callback, and the stdout also suggests it's being called.
The stdout :
.get()
1) should be called once
I am called
I am logging the data something
0 passing (15ms)
1 failing
1) .get() should be called once:
AssertionError: expected spy to have been called exactly once, but it was called 0 times
Details:
(a) baseRequest.get return the data as a bluebird promise. This can be used by passing in a nodeback to .get itself or by chaining .then after .get call.
BaseRequest.prototype.get = function(callback) {
// inner details
return invokeandPromisify(request,callback);
}
function invokeandPromisify(request, callback) {
return new Promise(function(resolve,reject) {
// Invoke the request
request.end(function(err,result) {
// Return the results as a promise
if (err || result.error) {
reject(err || result.error);
} else {
resolve(result);
}
});
}).nodeify(callback); // to have a node style callback
}
Does it happen because the callback on which I want to spy is passed to a different function( invokeandPromisify here ) and the spying is lost ? I am just interpreting this.
Regards.

Since baseRequest#get returns a promise, I would make the assertions after the promise is resolved.
See example below:
it('should be called once',function(done) {
// => Need to spy on this
var callback = function(err,data) {
console.log('I am called');
if (err) {
console.log('I am logging the error '+err);
} else {
console.log('I am logging the data '+data);
}
}
agentMock._response = {body:'something',headers:'headers'};
// => Start Spying
var spy = sinon.spy(callback);
sinon.spy(agentMock,'get');
baseRequest.get(spy).finally(function() {
expect(agentMock.get).to.have.been.calledOnce;
expect(spy).to.have.been.calledOnce;
expect(spy).to.have.been.calledWith(null,'data');
done();
});
});

Your test should be set as async by adding done. Then in your callback funtion call done()
Please check http://mochajs.org/#asynchronous-code

Related

Javascript testing - function called with specfic argument

I am trying to write a unit test for a function but cannot figure out how to check if it makes a call to a nested function with a specific argument. I am assuming I will need to use sinon alongside chai and mocha for this, but I could really use some help.
The function I would like to test looks like:
function myFunc(next, value) {
if (value === 1) {
const err = new Error('This sets an error');
next(err);
} else {
next();
}
}
I would like to test if next is called with or without the err variable. From what I read so far I should use a spy for this (I think) but how would I use that spy? Looking at this example from the Sinon docs it is unclear to me where PubSub comes from:
"test should call subscribers with message as first argument" : function () {
var message = "an example message";
var spy = sinon.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, message);
}
Source: https://sinonjs.org/releases/latest/assertions/
If you have a function like this
function myFunc(next, value) {
if (value === 1) {
const err = new Error('This sets an error');
next(err);
} else {
next();
}
}
The test could look like this
it ('should call the callback with an Error argument', function (done) {
const callback = (err) => {
if (err && err instanceof Error && err.message === 'This sets an error'){
// test passed, called with an Error arg
done();
} else {
// force fail the test, the `err` is not what we expect it to be
done(new Error('Assertion failed'));
}
}
// with second arg equal to `1`, it should call `callback` with an Error
myFunc(callback, 1);
});
so you don't necessarily need sinon for that

Using async / await inside async.auto chain leads to TypeError: callback is not a function

What version of async are you using?
2.6.1
Which environment did the issue occur in (Node version/browser version)
8.11.3
What did you do? Please include a minimal reproducible case illustrating the issue.
Assuming fileObj is supplied from outside:
async.auto({
download: (downloadCB) => {
if (fileObj) {
fs.writeFile(__dirname + ‘fileNew.txt’, fileObj.content, 'base64', function (err) {
if (err){
return downloadCB(err);
}
return downloadCB(null , fileObj.generatedFileName); // works fine
});
} else {
let err = new Error('File not found');
return downloadCB(err);
}
},
collectData: ['download', async (results, collectCB) => {
console.log(typeof collectCB); // prints undefined
console.log(typeof results); // prints correct object
let res = await anHttpRequest();
if (res.response && res.response.statusCode == 200) {
return collectCB(null , 'fileCombined.txt'); // This is where the ISSUE happens
}
else if(res.response.statusCode >= 300) {
return collectCB(new Error('Request failed inside async-auto'));
}
}],
filterData: ['collectData', (results, filterCB) => {
doFilter(results.collectData, filterCB);
}],
})
What did you expect to happen?
After collectData finishes execution, filterData should begin execution the param passed inside collectCB function
What was the actual result?
TypeError: collectCB is not a function.
The same code executes well with version 2.0.1 but after upgrade to 2.6.1 it has stopped working and its critical for us. Any work arounds will also be appreciated.
based on the documentation (quoted in the other answer already but here it is again)
Wherever we accept a Node-style async function, we also directly accept an ES2017 async function. In this case, the async function will not be passed a final callback argument, and any thrown error will be used as the err argument of the implicit callback, and the return value will be used as the result value. (i.e. a rejected of the returned Promise becomes the err callback argument, and a resolved value becomes the result.)
what you would do is
async.auto({
download: (downloadCB) => {
if (fileObj) {
fs.writeFile(__dirname + ‘fileNew.txt’, fileObj.content, 'base64', function(err) {
if (err) {
return downloadCB(err);
}
return downloadCB(null, fileObj.generatedFileName); // works fine
});
} else {
let err = new Error('File not found');
return downloadCB(err);
}
},
// Note, no callback as per documentation
collectData: ['download', async (results) => {
console.log(typeof results); // prints correct object
let res = await anHttpRequest();
if (res.response && res.response.statusCode == 200) {
// this return is equivalent to callback(null, value);
return 'fileCombined.txt';
} else if (res.response.statusCode >= 300) {
// this throw is equivalent to callback(err);
throw new Error('Request failed inside async-auto');
}
// but surely something should be here!? for status code 201-209?
}],
filterData: ['collectData', (results, filterCB) => {
doFilter(results.collectData, filterCB);
}],
})
Just a copy-paste from the official documentation:
Wherever we accept a Node-style async function, we also directly
accept an ES2017 async function. In this case, the async function will
not be passed a final callback argument, and any thrown error will be
used as the err argument of the implicit callback, and the return
value will be used as the result value. (i.e. a rejected of the
returned Promise becomes the err callback argument, and a resolved
value becomes the result.)

How to checking function called in Promise by sinon

I was using mocha + chai + sinon to test function, but in promise use called property, even if write a undefined function, called always true, What is it caused by ?and how to resolve? thx
describe("when click get verifycode", function () {
it('if get server response success, should start countdown', function (done) {
// given
sandbox.stub(model, 'getVerifyCode').resolves({});
sandbox.spy(view, 'startCountDown');
// when
var result = instance.onClickVerify('123456');
// then
result.then(res => {
// no matter what function, called always true
// e.g. expect(AnUndefinedFunction.called).to.be.ok;
expect(instance.view.startCountDown.called).to.be.ok;
done();
}).catch(err => {
done();
});
The problem here is that the .catch block is catching any error that occurs in the .then block.
This is an issue because you are calling done without passing the err object. Mocha interprets this as the test passing correctly, rather than failing.
The fix:
describe("when click get verifycode", function () {
it('if get server response success, should start countdown', function (done) {
// given
sandbox.stub(model, 'getVerifyCode').resolves({});
sandbox.spy(view, 'startCountDown');
// when
var result = instance.onClickVerify('123456');
// then
result.then(res => {
// no matter what function, called always true
// e.g. expect(AnUndefinedFunction.called).to.be.ok;
expect(instance.view.startCountDown.called).to.be.ok;
done();
}).catch(err => {
done(err);
});
})
});

In NodeJS how do I exit a parent function from a virtual function

I have something like the following
var async = require(async)
function start () {
async.series(
[
function (callback) {
// do something
callback(null, "Done doing something")
return // EXIT start() NOT JUST THIS VIRTUAL FUNCTION
},
function (callback) {
// do something else if the first thing succeeded
callback(null, "Done doing something else")
}
],
function (err,result) {
if (err) {
console.log(err)
}
}
)
console.log("All done!")
}
// Start doing stuff
start()
Is it possible to exit the function start() based on some condition from within the contained virtual function where I placed the comment "EXIT start() NOT JUST THIS VIRTUAL FUNCTION"
In short yes, you can exit the task series early. async uses error-first callbacks, which are a fundamental async callback structure in Node.js. An additional resource for learning about Async development using Node.js see this great blog post from RisingStack.
Callbacks from async get returned to a final callback that is called when the async.series is completed if supplied, this callback is optional. If a task within the series returns the callback with a value other than null in the first parameter, the error parameter, the series will break and return to the final callback handler.
async.series(
[
function (callback) {
// If some condition is met, exit series with an error
if (someCondition)
return callback('exit start', null);
return callback(null, 'Done doing something');
},
function (callback) {
if (someCondition)
return callback('error', null);
// do something else if the first thing succeeded
return callback(null, "Done doing something else")
}
],
function (err,result) {
// Handle error returned from a series callback if one occurred
if (err) {
console.log(err)
}
}
)
In async document (here: https://github.com/caolan/async#seriestasks-callback)
async.series
Run the functions in the tasks collection in series, each one running
once the previous function has completed. If any functions in the
series pass an error to its callback, no more functions are run, and
callback is immediately called with the value of the error. Otherwise,
callback receives an array of results when tasks have completed.
So, instead of calling:
return // EXIT start() NOT JUST THIS VIRTUAL FUNCTION
You can just call:
callback("stop", null)

How can you retry after an exception in Javascript when using promises?

I'm using the Bluebird promise library. I have a chain of promisified functions like the following:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.spread(function(response, data) {
return sendResponseAsync(response);
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// handle error here
});
Occasionally sendMessage will fail because, let's say, the server to respond to isn't available. I want the code to keep on trying to respond forever until it succeeds. You can't simply wrap the sendMessage in a catch because it doesn't actually throw an exception, I suppose, it calls the "error" function which, in this promisified code is the "catch" at the bottom. So there must be some way to "retry" send message in the "catch" section. The problem is that even if I retry in a loop in the "catch" I still have no way to jump up to the promise chain and execute the remaining promisified functions. How do I deal with this?
EDIT:
My retry for a HTTP post ended up looking like this:
function retry(func) {
return func()
.spread(function(httpResponse) {
if (httpResponse.statusCode != 200) {
Log.error("HTTP post returned error status: "+httpResponse.statusCode);
Sleep.sleep(5);
return retry(func);
}
})
.catch(function(err) {
Log.err("Unable to send response via HTTP");
Sleep.sleep(5);
return retry(func);
});
}
Here's a sample retry function (not yet tested):
function retry(maxRetries, fn) {
return fn().catch(function(err) {
if (maxRetries <= 0) {
throw err;
}
return retry(maxRetries - 1, fn);
});
}
The idea is that you can wrap a function that returns a promise with something that will catch and retry on error until running out of retries. So if you're going to retry sendResponseAsync:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.spread(function(response, data) {
return retry(3, function () { return sendResponseAsync(response); });
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// handle error here
});
Since the retry promise won't actually throw until all retries have been exhausted, your call chain can continue.
Edit:
Of course, you could always loop forever if you preferred:
function retryForever(fn) {
return fn().catch(function(err) {
return retryForever(fn);
});
}
Here is a small helper that acts like then but retries the function.
Promise.prototype.retry = function retry(onFulfilled, onRejected, n){
n = n || 3; // default to 3 retries
return this.then(function(result) {
return Promise.try(function(){
return onFulfilled(result); // guard against synchronous errors too
}).catch(function(err){
if(n <= 0) throw err;
return this.retry(onFulfilled, onRejected, n - 1);
}.bind(this)); // keep `this` value
}.bind(this), onRejected);
};
Which would let you write your code prettier like:
receiveMessageAsync(params)
.then(function(data)) {
return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
return [response, deleteMessageAsync(request)];
})
.retry(function(response, data) {
return sendResponseAsync(response); // will retry this 3 times
})
.then(function(data) {
return waitForMessage(data);
})
.catch (function(err) {
// I don't like catch alls :/ Consider using `.error` instead.
});
I just released https://github.com/zyklus/promise-repeat, which retries a promise until it either times out or a maximum number of attempts are hit. It allows you to write:
receiveMessageAsync(params)
...
.spread(retry(
function(response, data) {
return sendResponseAsync(response);
}
))
...

Categories

Resources