So, I'm testing a component that relies on an event-emitter. To do so I came up with a solution using Promises with Mocha+Chai:
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
done();
}).catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
});
On the console I'm getting an 'UnhandledPromiseRejectionWarning' even though the reject function is getting called since it instantly shows the message 'AssertionError: Promise error'
(node:25754) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): AssertionError: Promise error: expected { Object (message, showDiff, ...) } to be falsy
- should transition with the correct event
And then, after 2 sec I get
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
Which is even weirder since the catch callback was executed(I think that for some reason the assert failure prevented the rest of the execution)
Now the funny thing, if I comment out the assert.isNotOk(error...)
the test runs fine without any warning in the console. It stills 'fails' in the sense that it executes the catch.
But still, I can't understand these errors with promise. Can someone enlighten me?
This question is related to
javascript
node.js
promise
mocha.js
chai
For those who are looking for the error/warning UnhandledPromiseRejectionWarning
outside of a testing environment, It could be probably because nobody in the code is taking care of the eventual error in a promise:
For instance, this code will show the warning reported in this question:
new Promise((resolve, reject) => {
return reject('Error reason!');
});
(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!
and adding the .catch()
or handling the error should solve the warning/error
new Promise((resolve, reject) => {
return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });
Or using the second parameter in the then
function
new Promise((resolve, reject) => {
return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
In case you have an async helperFunction()
called from your test... (one explicilty with the ES7 async
keyword, I mean)
? make sure, you call that as await helperFunction(whateverParams)
(well, yeah, naturally, once you know...)
And for that to work (to avoid ‘await is a reserved word’), your test-function must have an outer async marker:
it('my test', async () => { ...
I faced this issue:
(node:1131004) UnhandledPromiseRejectionWarning: Unhandled promise rejection (re jection id: 1): TypeError: res.json is not a function (node:1131004) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.j s process with a non-zero exit code.
It was my mistake, I was replacing res
object in then(function(res)
, so changed res
to result and now it is working.
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(res){//issue was here, res overwrite
return res.json(res);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
module.exports.update = function(req, res){
return Services.User.update(req.body)
.then(function(result){//res replaced with result
return res.json(result);
}, function(error){
return res.json({error:error.message});
}).catch(function () {
console.log("Promise Rejected");
});
Service code:
function update(data){
var id = new require('mongodb').ObjectID(data._id);
userData = {
name:data.name,
email:data.email,
phone: data.phone
};
return collection.findAndModify(
{_id:id}, // query
[['_id','asc']], // sort order
{$set: userData}, // replacement
{ "new": true }
).then(function(doc) {
if(!doc)
throw new Error('Record not updated.');
return doc.value;
});
}
module.exports = {
update:update
}
I got this error when stubbing with sinon.
The fix is to use npm package sinon-as-promised when resolving or rejecting promises with stubs.
Instead of ...
sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))
Use ...
require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));
There is also a resolves method (note the s on the end).
See http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections
The assertion libraries in Mocha work by throwing an error if the assertion was not correct. Throwing an error results in a rejected promise, even when thrown in the executor function provided to the catch
method.
.catch((error) => {
assert.isNotOk(error,'Promise error');
done();
});
In the above code the error
objected evaluates to true
so the assertion library throws an error... which is never caught. As a result of the error the done
method is never called. Mocha's done
callback accepts these errors, so you can simply end all promise chains in Mocha with .then(done,done)
. This ensures that the done method is always called and the error would be reported the same way as when Mocha catches the assertion's error in synchronous code.
it('should transition with the correct event', (done) => {
const cFSM = new CharacterFSM({}, emitter, transitions);
let timeout = null;
let resolved = false;
new Promise((resolve, reject) => {
emitter.once('action', resolve);
emitter.emit('done', {});
timeout = setTimeout(() => {
if (!resolved) {
reject('Timedout!');
}
clearTimeout(timeout);
}, 100);
}).then(((state) => {
resolved = true;
assert(state.action === 'DONE', 'should change state');
})).then(done,done);
});
I give credit to this article for the idea of using .then(done,done) when testing promises in Mocha.
I had a similar experience with Chai-Webdriver for Selenium.
I added await
to the assertion and it fixed the issue:
Example using Cucumberjs:
Then(/I see heading with the text of Tasks/, async function() {
await chai.expect('h1').dom.to.contain.text('Tasks');
});
Source: Stackoverflow.com