I have an event handler function that takes some event data and a callback function as input.
This event handler is using a promise to do its job:
function myHandler(event, callback) {
somePromise(event).then((result) => {
if (result.Error) {
callback(error);
} else {
callback(null, result.SuccessData);
}
});
}
I have the following code to test the handler:
it('test handler', function(done) {
let event = {...};
myHandler(event, function(error, success) {
expect(success).to.not.be.null;
expect(error).to.be.null;
expect(success.member).to.be.equal('expected');
done();
}
});
When running this test, I receive this error:
(node:3508) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): AssertionError: expected 'unexpected' to equal 'expected'
and a the end of all the tests:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
but yet the test is still passing...
Why is this error occuring while the done() function is called?
Wrap your test into a promise that rejects if the assertion fails.
it('test handler', () => {
let event = {...}
return new Promise((resolve, reject) => {
myHandler(event, (error, success) => {
try {
expect(success).to.not.be.null;
expect(error).to.be.null;
expect(success.member).to.be.equal('expected');
resolve();
} catch (err) {
reject(err);
}
});
});
});
You're using Promises.
You can either return your Promise without using done, like this:
// Note the absence of the done callback here
it('test handler', function() {
let event = {...};
return myHandler(event, function(error, success) {
expect(success).to.not.be.null;
expect(error).to.be.null;
expect(success.member).to.be.equal('expected');
}
});
Or use Chai As Promised:
it('test handler', function(done) {
let event = {...};
myHandler(event, function(error, success) {
expect(success).to.not.be.null;
expect(error).to.be.null;
expect(success.member).to.be.equal('expected');
}.should.notify(done)
});
The later seems better to me, as if you forget the return in the first example, your tests can silently fail.
Related
I am unable to use a promise correctly when working with mocha (command : mocha --reporter spec --recursive --timeout 60000)
Getting errors like :
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
Error: Timeout of 60000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (C:\Users\GB\Documents\projects\requireurl\concurrency\test\test_demos_cluster.js)
at listOnTimeout (node:internal/timers:564:17)
const expect = require('chai').expect;
describe('test-.mjs::concurrency.js: Test Suite for concurrency.js Files', function () {
var result
before(async function (done) {
function testPromise() {
return new Promise(function (resolve, reject) {
resolve({ msg: "testing" });
})
}
result = await testPromise(); // return a promise with result
done()
});
describe('test-.js::concurrency.js: [Test A] Test Suite for concurrency.js in main repo directory', function () {
it('[Test A] Test for ', function (done) {
// expect(100).to.equal(100);
expect(result.msg).to.equal("testing");
done();
});
});
});
The error is self-explanatory:
Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
Either use async or done
before(async function () {
function testPromise() {
return new Promise(function (resolve, reject) {
resolve({ msg: "testing" });
})
}
result = await testPromise(); // return a promise with result
});
or
before(function (done) {
function testPromise() {
return new Promise(function (resolve, reject) {
resolve({ msg: "testing" });
})
}
testPromise().then(() => done());
});
My unit test code is like this:
// function for my unit test to test exception
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
})();
});
createContextAndDoc().catch((e) => {
console.log('--------');
console.log(e.message);
console.log('--------');
});
When i run this unit test:
./node_modules/.bin/jest local_modules/__test__/unhandledException.test.js
The complete output is like this:
RUNS local_modules/__test__/unhandledException.test.js
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Error: unit test error message".] {
code: 'ERR_UNHANDLED_REJECTION'
}
Don't see why it says unhandled, i do have .catch(). Any suggestions ?
Looks like you're invoking the async function right away, which causes the unit test error, and that is never cought since the promise is never returned - so the .catch is not catching anything. The error is not part of the promise chain.
If you want to invoke the function right away you need to catch the error and reject it so the promise finishes.
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = () => new Promise((resolve, reject) => {
(async () => {
try {
const res = await mockServiceThrow();
if (res === 1) resolve(1)
else reject(0);
} catch (e) {
reject(e)
}
})();
});
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
You could also simplify your code a little:
const mockServiceThrow = async () => { throw new Error('unit test error message'); };
const createContextAndDoc = async () => {
const res = await mockServiceThrow();
if(res === 1) {
return Promise.resolve(1)
} else {
return Promise.reject(0);
}
};
createContextAndDoc().catch(e => {
console.log('------------------');
console.log(e.message);
console.log('--------')
})
Edit: Further explanation of promises.
createContextAndDoc is a function that returns a promise. That promise resolves if res === 1 but rejects if res is something else. For that to happen the mockServiceThrow promised must be resolved. Otherwise you won't get any value for res variable.If that happens then your promise function never fullfills (resolves or rejects).
In your case mockServiceThrow fails and throws an error, this error is not part of the promise you created with new Promis. To make sure your promise fullfills (resolves or rejects) you need the callbacks, otherwise the error is not part of the promise.
The simplified code has one async function so that when mockServiceThrow fails its part of the async function that you're trying to catch.
Code:
var processFooBar = function (message, callback) {
doFooAndBar(message, callback);
};
module.exports.processFooBar = processFooBar;
var doFooAndBar = function (data, callback) {
async.parallel(
[
function (callback) {
foo(data, function (err, response) {
callback(err, response);
});
},
function (callback) {
bar(data, function (err, response){
callback(err, response);
});
}
],
function (err, results) {
callback(err, results);
}
);
};
module.exports.doFooBar = doFooBar;
Unit Test
describe('Process data', function () {
var fooStub;
beforeEach(function (done) {
done();
});
afterEach(function (done) {
fooStub.restore();
done();
});
it('can process data', function (done) {
fooStub = sinon.stub(fileName, 'foo').yields(null, null);
barNockCall();
app.processFooBar(message,
function (err, response) {
nock.isDone().should.be.true;
nock.cleanAll();
done();
}
}
});
I am getting the following error:
can process data:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
(/Path/To/Test.js)
If I remove foo() in async.parallel, then I don't get the error. Also, I guess the first sinon.stub that is fooStubis not getting called.
You need to increase the test framework's timeout. It might have default timeout of 2000ms and will throw error if the request takes more than 2 sec.
beforeEach(function (done) {
this.timeout(10000)
done();
});
Overriding the default timeout might work in your case.
Getting an exception in my Node JS Express application and cannot figure out why. I was getting the same error with my "real" code, so I found this code online and made a test router to see if the error occurred with known good code. Code runs fine in plnkr without the router.post line. Could this be because of the function in the first line? Any help is greatly appreciated.
router.post('/addTableTest', (function (req, res, next) {
let promise1 = new Promise((resolve, reject) => {
let data = false;
if (data) {
resolve('Data');
}
if (!data) {
reject('Not Data');
}
})
promise1.then((message) => {
console.log(message);
}).catch((message) => {
console.log(message);
})
}));
The closure executed by new Promise() is executed synchronously, i.e. immediately after the Promise has been created and before new returns. As your closure has been written to fail immediately and you can't attach a .catch() to it before new returns, you get an unhandled Promise rejection exception.
To make your code work you need to
start a Promise chain by creating a resolved Promise
attach a .then() clause to wrap your synchronous code
replace resolve(X) with return X
replace reject(X) with throw new Error(X)
Now you can safely attach the other Promise clauses, because the code in the just created Promise chain won't be executed until the closure that has created it leaves.
router.post('/addTableTest', (req, res, next) => {
let promise1 = Promise.resolve()
.then(() => {
let data = false; // i.e. the promise will reject
if (data) {
return 'Data';
} else {
throw new Error('Not Data');
}
});
promise1
.then(message => {
console.log(message);
})
.catch(error => {
console.log(error.message);
});
});
Is it necessary to return a Promise function if I'm using await & async in Node 8+?
async function _readSourceDataFromCache (slug_name) {
aerospikeClient = aerospikeConf.AerospikeClient;
console.log('In async function')
console.log(aerospikeClient);
return new Promise ( function (resolve, reject) {
aerospikeClient.get(aerospikeConf.AerospikeKey, function (error, record) {
if (error) {
switch (error.code) {
case aerospikeConf.Aerospike.status.AEROSPIKE_ERR_RECORD_NOT_FOUND:
console.log('NOT_FOUND -', aerospikeConf.AerospikeKey)
break
default:
console.log('ERR - ', error, aerospikeConf.AerospikeKey)
}
resolve(false)
}
else{
resp = record['value']
aerospikeClient.close();
return resolve(resp);
}
});
})
}
How do I handle this from the calling function?
You should return your promise (I think you can use that promise to use something)
But async is not necessary.
async mark you should wait promise resolved with await
async yourFunction(){
console.log('Something');
await readDataBase(); // wait this promise return it's result
console.log('Oh, done!!!')
}
Your function return a Promise, and don't wait it done.
If you want wait it, please use async for functionParent (this function call your function).