I am trying to test a function but my assert statement always runs before my actual code. I'm testing the function createAppointment
My test:
describe("Appointments", function() {
it("should be able to create a new appointment", function(err) {
let newAppts = [];
testRequests.forEach(request => {
createAppointment(testDb, request, function(err, id) {
if (err) return err;
newAppts.push(id);
return id;
});
});
assert.equal(newAppts.length, 5);
});
});
I am expecting the length of newAppts to be 5 but it returns 0 every time since the assert runs before the forEach is done. What is the best to to go about adding a callback?
Here's a test that works for me:
describe("Appointments", function () {
it("should be able to create 5 new appointments", function (done) {
// promisify createAppointment, build an array, pass to Promise.all()
Promise.all(testRequests.map(request => new Promise((resolve, reject) => {
createAppointment(testDb, request, function (err, id) {
if (err) reject(err);
resolve(id);
});
})))
.then(newAppts => {
// if all Promises resolve, check result
assert.equal(newAppts.length, 5);
// and tell mocha we're done
done();
})
.catch(err => {
// if any one of the Promises fails, we're also done
done(err);
});
});
});
If you simply want to test async code, it's enough to return the Promise. This will simply check if it resolves.
If your test is supposed to check the result though, don't return the Promise; instead use the it callback's done parameter to tell mocha that the async test has finished, after calling assert to check the result.
Related
Desperately trying to write a sync version of https://www.npmjs.com/package/node-firebird#reading-blobs-aasynchronous
Basically I need to (a)wait twice:
for the callback function to execute so that the eventEmitter is available
for the "end" event to occur
and then return the Buffer.
my code (JS/TS mix for now) currently does 2, but not 1 : readBlob returns undefined, then Buffer.concat(buffers) is called later ... :
function readBLOB(callback: any): Buffer {
return callback(async (err, _, eventEmitter) => {
let buffers = []
if (err)
throw err
eventEmitter.on('data', chunk => {
buffers.push(chunk);
});
return await eventEmitter.once('end', function (e) {
return Buffer.concat(buffers)
})
})
}
Sorry to ask one more time (yes, I checked a lot of other questions and tried a lot of things...), but how to make this work (simply...) ?
(the function that calls the callback is fetch_blob_async in https://github.com/hgourvest/node-firebird/blob/master/lib/index.js#L4261 , just in case...)
There are few mistakes here like returning an callback function, witch returns, i guess, undefined or returning something IN an callback function that makes no sense.
Also async / await makes no sense here it has no effect. async / await is only useful if you want to await till some Promise resolves. But you have no Promise in your code at all.
What you need is new Promise
function readBLOB(callback) {
return new Promise((resolve, reject) => {
callback((err, _, eventEmitter) => {
let buffers = [];
if (err) reject(err);
eventEmitter.on("data", chunk => {
buffers.push(chunk);
});
eventEmitter.once("end", function(e) {
resolve(Buffer.concat(buffers));
});
});
});
}
Simple like that. You resolve your Buffer and reject if some error occurs
Now you can use it like:
readBLOB(cb).then(data => {
console.log(data);
})
I need to iterate an array in javascript with some values that will be used to call an asynchronous function that returns a promise. I can´t continue the next code section without all promises were completed.
In the following example, the function "processInvoices" has to resolve a promise until all promises inside were completed (assume that "confirmInvoice" is an asynchronous function that has different response times):
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
invoices.forEach(number=>
{
confirmInvoice(number)
.then(result=>{
if (!result)
{reject('Error confirming the invoice');}
});
});
resolve(true); // Resolving here doesn´t mean that all promises were completed!
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
if (result) // It´s probable that the following message isn´t accurate:
console.log('All invoices were processed');
}).catch(reason=>{console.error(reason)});
}
With the code above, I can´t be sure that the "console.log (or any routine)" will be executed right away after all promises were completed.
UPDATE Promise.all(iterable) solves the problem :
processInvoices(invoices)
{
return new promise((resolve,reject)=>
{
var promisesArray = []; // store all promises here
invoices.forEach(number=>
{
promisesArray.push(confirmInvoice(number));
});
Promise.all(promisesArray).then (results=>{
// validate all results and reject if necessary...
if (validateResults(results)) {
// if all ok
resolve('All invoices were processed successfully');
}
else {
reject('Error processing one or more invoices'); // just for demo purposes
}
});
});
}
init() // triggered at load..
{
let invoices = [2,4,8,16,31];
processInvoices(invoices)
.then(result=>
{
console.log(result);
}).catch(reason=>{console.error(reason)});
}
forEach runs synchronously. If you want to wait for all Promises to resolve before the full processInvoices resolves, you should use Promise.all instead; map each invoice number to a Promise and call Promise.all on the resulting array of Promises. Also, your
if (!result) {resolve(false);}
sounds like it's an attempt to handle an error, in case there is no result - in this case, you should reject the Promise instead of calling resolve. Ideally, the failed confirmInvoice call would result in a rejected Promise, but if that's not something you can fix, throw an error if result is falsey so you can handle it in a catch in init. For example:
function processInvoices(invoices) {
return Promise.all(
invoices.map(number => (
confirmInvoice(number)
.then(result => {
// This will result in the `Promise.all` rejecting:
if (!result) throw new Error('Failed to confirm invoice ' + number);
})
))
);
}
function init() {
const invoices = [2, 4, 8, 16, 31];
processInvoices(invoices)
.then(result => {
console.log('All invoices were processed');
})
.catch((err) => {
console.log('err: ' + err);
});
}
I am trying to unzip a file first and then await for that unzipping file to be complete before i loop through each file and upload it to an S3 bucket. The first function unzipPromise is running fine, and everything is getting unzipped in the proper directory, but the uploadS3Promise is not running at all. I am not getting errors through this process, it just runs and unzips the file and never touches the uploadS3Promise function.
function unzipInput(file, client, project_number, oldpath, newpath) {
path = `zips/${client}/${project_number}/${file}/`;
function unzipPromise() {
return new Promise((resolve, reject) => {
fse.mkdirsSync(path);
fs.rename(oldpath, newpath, err => {
if (err) {
throw err;
}
});
fs.createReadStream(newpath).pipe(unzip.Extract({ path }));
});
}
function uploadS3Promise() {
console.log("running");
return new Promise((resolve, reject) => {
// fs.unlinkSync(newpath);
fs.readdirSync(newpath).forEach(file => {
uploadToS3(file, client, project_number, path);
console.log(file, "test");
});
if (err) reject(err);
else resolve("success");
});
}
// New code with async:
(async () => {
try {
await unzipPromise();
await uploadS3Promise();
} catch (e) {
console.error(e);
}
})();
}
uploadS3Promise doesn't run because the code is still awaiting for unzipPromise to complete. The code will not execute further unless you resolve or reject a promise.
So in you code ...
function unzipPromise(){
...
resolve(...)
...
}
On an unrelated note, I think it would be more readable not to name function names end in promise. Like just call them unzip and uploadS3. We don't name our function usually by return types right, like we never say intIndexOf, and so on.
You supposed to call resolve after unzip path is done or reject if error occured.
And since streams are EventEmitter, you can listen to events and interact with it
const stream = fs.createReadStream(newpath).pipe(unzip.Extract({ path }))
stream
.on('error', (err) => reject(err))
.on('finish', () => resolve())
Personally, I'd use a .then to break the process down a bit.
unzipPromise().then(res => {
console.log('resolved promise 1');
uploadS3Promise();
} rej => {
console.log('rejected promise 1, sorry!');
});
Also- "unzipInput" is never resolved or rejected.
I have a function that returns an ES6 promise for use in another external promise chain. The function scans for wifi networks using child_process.exec. The output from exec is sent via the callback to a synchronous parsing function that will return the output in a formatted object, and then resolve the outer promise:
var exec = require('child_process').exec;
function scan() {
return new Promise((resolve, reject) => {
// getCurrent returns a promise
getCurrent().then(network => {
exec('iwlist wlan0 scan', function(err, stdout, stderr) {
(err) ? reject(stderr) : resolve(parseOutput(stdout, network));
});
})
.catch(err => {
reject(err);
});
});
}
The problem is, I can't seem to get sinon stubs working properly to get the stubbed version of exec to be called. Currently I have something along the lines of:
var wifi = require('./wifi');
describe('#scan', function() {
it('Should scan and return networks object', function() {
var fakeNetwork = '(fake iwlist output goes here)';
var exec = sinon.stub(child_process, 'exec').yields(false, fakeNetwork);
var getCurrent = sinon.stub(wifi, 'getCurrent').returns(Promise.resolve('current-ssid'));
wifi.scan().then(function(networks) {
console.log(networks);
sinon.assert.calledOnce(getCurrent);
sinon.assert.calledOnce(exec);
});
getCurrent.restore();
exec.restore();
});
});
I have also tried stubbing like:
var getCurrent = sinon.stub(wifi, 'getCurrent', function() {
return Promise.resolve('current-ssid').then(network => {
exec('iwlist wlan0 scan', function(err, data) {
return(wifi.parseOutput(data, network));
});
});
});
The behavior seems to be that either exec never gets called, or more strangely, that the stubbed exec does get called, but the 'real' functions get called anyway.
With all of the stubs, all I am really "testing" is the parseOutput method, so should I just ditch testing the 'scan' function completely and only focus on the parse function (which would be simple to test), or is there another way I should be looking at this?
I believe I am just doing something obviously wrong, and have been hitting a wall here for several hours. I'm hoping someone has run into this before and can suggest an alternative/better approach.
you can use stub.yields([arg1, arg2, ...]) sinon stubs guide
here is my code
function getVersioniList() {
let versionList = new Promise((resolve, reject) => {
child_process.exec(cmd, function(error, stdout, stderr) {
if (error) {
reject(error);
}
resolve(stdout);
});
});
return versionList;
}
and unit test code as below
// i like the sandbox, or you can use sinon itself
sandbox = sinon.sandbox.create();
sandbox.stub(child_process, 'exec').yields(undefined, [1, 2, 3]);
assert.equal(await getVersioniList(), [1, 2, 3]);
finally, you can get versionList [1, 2, 3]
I am new to mocha/sinon/promises.One test I wrote with mock on a function that returns a promise really confuses me. I created a complete example below, which at this point is a little contrived because I was trying to strip this down to the bare minimum that shows the issue.
'use strict';
const sinon = require('sinon');
var _fakeGetAndPost = {
get: (cmd, cb) => cb("", cmd),
post: (cmd, cb) => cb("", cmd)
};
function getCardsFromList(specificGetCardsCmd) {
return new Promise((resolve, reject) => {
_fakeGetAndPost.get(specificGetCardsCmd, (err, response) => {
if (err) {
reject(err);
} else {
resolve(response);
}
});
});
}
describe("Experiment with mock", () => {
it("The mock passes with both once() and never() - but shouldn't", () => {
var myMock = sinon.mock(_fakeGetAndPost);
myMock.expects('get').once(); //never() also passes, /twice() fails
getCardsFromList("junk");
myMock.verify();
myMock.restore();
});
});
The confusion arises from the myMock.expects('get').once(); line. If I replace the expectation of once() with twice() the test fails as expected. However, if I replace once() with never() the code still passes. I was surprised by this and don't understand how or why this can happen. If twice() had also passed I would have suspected that mocha wasn't waiting for the promise but since twice() fails it looks like that mechanism works.