Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai

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

  1. 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?

like image 572
Jzop Avatar asked Sep 27 '16 05:09

Jzop


People also ask

How do I fix UnhandledPromiseRejectionWarning error?

To fix UnhandledPromiseRejectionWarning: This error originated either by throwing inside of an async function without a catch block with JavaScript, we add a catch block. router. get("/emailfetch", authCheck, async (req, res) => { try { let emailFetch = await gmaiLHelper.

What is UnhandledPromiseRejectionWarning?

The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value. The Promise. reject() method returns a Promise object that is rejected with a given reason.


2 Answers

The issue is caused by this:

.catch((error) => {   assert.isNotOk(error,'Promise error');   done(); }); 

If the assertion fails, it will throw an error. This error will cause done() never to get called, because the code errored out before it. That's what causes the timeout.

The "Unhandled promise rejection" is also caused by the failed assertion, because if an error is thrown in a catch() handler, and there isn't a subsequent catch() handler, the error will get swallowed (as explained in this article). The UnhandledPromiseRejectionWarning warning is alerting you to this fact.

In general, if you want to test promise-based code in Mocha, you should rely on the fact that Mocha itself can handle promises already. You shouldn't use done(), but instead, return a promise from your test. Mocha will then catch any errors itself.

Like this:

it('should transition with the correct event', () => {   ...   return new Promise((resolve, reject) => {     ...   }).then((state) => {     assert(state.action === 'DONE', 'should change state');   })   .catch((error) => {     assert.isNotOk(error,'Promise error');   }); }); 
like image 179
robertklep Avatar answered Nov 13 '22 22:11

robertklep


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 */ }); 
like image 31
gsalgadotoledo Avatar answered Nov 14 '22 00:11

gsalgadotoledo