NodeJS UnhandledPromiseRejectionWarning

134

Donc, je teste un composant qui repose sur un émetteur d'événement. Pour ce faire, j'ai trouvé une solution utilisant Promises avec 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();
  });
});

Sur la console, j'obtiens un 'UnhandledPromiseRejectionWarning' même si la fonction de rejet est appelée car elle affiche instantanément le message 'AssertionError: Promise error'

(node: 25754) UnhandledPromiseRejectionWarning: Rejet de promesse non géré (identifiant de rejet: 2): AssertionError: Promise error: expected {Object (message, showDiff, ...)} to be false 1) should transition with the correct event

Et puis, après 2 secondes, je reçois

Erreur: dépassement du délai de 2000 ms. Assurez-vous que le rappel done () est appelé dans ce test.

Ce qui est encore plus étrange depuis que le rappel catch a été exécuté (je pense que pour une raison quelconque, l'échec d'assertion a empêché le reste de l'exécution)

Maintenant, ce qui est drôle, si je commente assert.isNotOk(error...)le test, le test se déroule correctement sans aucun avertissement dans la console. Il «échoue» toujours dans le sens où il exécute la capture.
Mais encore, je ne peux pas comprendre ces erreurs avec promesse. Quelqu'un peut-il m'éclairer?

Jzop
la source
Je pense que vous avez un ensemble supplémentaire d'accolades fermantes et de parenthèses à la toute dernière ligne. Veuillez les supprimer et réessayer.
Redu le
4
C'est tellement cool que le nouvel avertissement de rejet non géré trouve des bogues dans la vraie vie et fait gagner du temps aux gens. Tant de victoires ici. Sans cet avertissement, vos tests auraient expiré sans aucune explication.
Benjamin Gruenbaum le

Réponses:

161

Le problème est causé par ceci:

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

Si l'assertion échoue, elle lèvera une erreur. Cette erreur entraînera de done()ne jamais être appelé, car le code s'est trompé avant elle. C'est ce qui cause le délai.

Le «rejet de promesse non gérée» est également causé par l'assertion ayant échoué, car si une erreur est renvoyée dans un catch()gestionnaire et qu'il n'y a pas de catch()gestionnaire suivant , l'erreur sera avalée (comme expliqué dans cet article ). L' UnhandledPromiseRejectionWarningavertissement vous alerte sur ce fait.

En général, si vous souhaitez tester du code basé sur des promesses dans Mocha, vous devez vous fier au fait que Mocha lui-même peut déjà gérer les promesses. Vous ne devriez pas utiliser done(), mais plutôt renvoyer une promesse de votre test. Mocha détectera alors toutes les erreurs lui-même.

Comme ça:

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');
  });
});
robertklep
la source
7
Pour toute personne curieuse, cela vaut également pour le jasmin.
Nick Radford
@robertklep Le catch ne sera-t-il pas appelé pour toute erreur et pas seulement celle que vous attendez? Je pense que ce style ne fonctionnera pas si vous essayez d'affirmer des échecs.
TheCrazyProgrammer
1
@TheCrazyProgrammer le catchgestionnaire devrait probablement être passé comme deuxième argument à then. Cependant, je ne suis pas tout à fait sûr de l'intention du PO, alors je l'ai laissé tel quel.
robertklep
1
Et aussi pour toute personne curieuse de jasmin, utilisez done.fail('msg')dans ce cas.
Paweł
pouvez-vous donner un exemple complet de l'endroit où le code principal par rapport aux assertions devrait aller, pas si clair ici ..... surtout si vous affirmez une valeur de l'appel de service / promesse d'origine dans notre code. La promesse réelle de notre code s'inscrit-elle dans cette autre "promesse moka"?
bjm88
10

J'ai eu cette erreur en stubbing avec sinon.

Le correctif consiste à utiliser le package npm sinon-as-promised lors de la résolution ou du rejet de promesses avec des stubs.

Au lieu de ...

sinon.stub(Database, 'connect').returns(Promise.reject( Error('oops') ))

Utilisation ...

require('sinon-as-promised');
sinon.stub(Database, 'connect').rejects(Error('oops'));

Il existe également une méthode de résolution (notez le s à la fin).

Voir http://clarkdave.net/2016/09/node-v6-6-and-asynchronously-handled-promise-rejections

danday74
la source
1
Sinon inclut maintenant les méthodes «résout» et «rejette» pour les stubs à partir de la version 2. Voir npmjs.com/package/sinon-as-promised . J'ai quand même + 1 répondu à la réponse - je ne savais pas à ce sujet.
Andrew
9

Les bibliothèques d'assertions de Mocha fonctionnent en lançant une erreur si l'assertion n'était pas correcte. Lancer une erreur entraîne le rejet d'une promesse, même lorsqu'elle est lancée dans la fonction exécuteur fournie à la catchméthode.

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

Dans le code ci-dessus, l'objet est errorévalué trueainsi la bibliothèque d'assertions renvoie une erreur ... qui n'est jamais interceptée. En raison de l'erreur, la doneméthode n'est jamais appelée. Le donerappel de Mocha accepte ces erreurs, vous pouvez donc simplement terminer toutes les chaînes de promesses dans Mocha avec .then(done,done). Cela garantit que la méthode done est toujours appelée et que l'erreur sera signalée de la même manière que lorsque Mocha détecte l'erreur de l'assertion en code synchrone.

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);
});

Je donne crédit à cet article pour l'idée d'utiliser .then (done, done) lors du test des promesses dans Mocha.

Matthew Orlando
la source
6

Pour ceux qui recherchent l'erreur / l'avertissement en UnhandledPromiseRejectionWarningdehors d'un environnement de test, cela pourrait probablement être dû au fait que personne dans le code ne s'occupe de l'erreur éventuelle dans une promesse:

Par exemple, ce code affichera l'avertissement signalé dans cette question:

new Promise((resolve, reject) => {
  return reject('Error reason!');
});

(node:XXXX) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Error reason!

et l'ajout de .catch()ou la gestion de l'erreur devrait résoudre l'avertissement / l'erreur

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).catch(() => { /* do whatever you want here */ });

Ou en utilisant le deuxième paramètre dans la thenfonction

new Promise((resolve, reject) => {
  return reject('Error reason!');
}).then(null, () => { /* do whatever you want here */ });
gsalgadotoledo
la source
1
Bien sûr, mais je pense que dans la vie réelle, nous n'utilisons généralement pas juste new Promise((resolve, reject) => { return reject('Error reason!'); })mais en fonction, function test() { return new Promise((resolve, reject) => { return reject('Error reason!'); });}donc à l'intérieur de la fonction, nous n'avons pas besoin d'utiliser .catch()mais pour gérer avec succès les erreurs, il suffit de l'utiliser lors de l'appel de cette fonction test().catch(e => console.log(e))ou de la version async / try { await test() } catch (e) { console.log(e) }
await
1

J'ai fait face à ce problème:

(node: 1131004) UnhandledPromiseRejectionWarning: Rejet de promesse non gérée (id de rejet: 1): TypeError: res.json n'est pas une fonction (node: 1131004) DeprecationWarning: Les rejets de promesse non gérées sont obsolètes. Dans le futur, les rejets de promesse qui ne sont pas traités mettront fin au processus de Node.j avec un code de sortie différent de zéro.

C'était mon erreur, je remplaçais l' resobjet dans then(function(res), donc changé resen résultat et maintenant cela fonctionne.

Faux

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");
              });

Correction

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");
              });

Code de service:

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
}
Muhammad Shahzad
la source
1

Voici mon expérience avec E7 async / await :

Au cas où vous auriez un async helperFunction()appelé de votre test ... (une explicabilité avec le asyncmot-clé ES7 , je veux dire)

→ assurez-vous que vous appelez ça comme await helperFunction(whateverParams) (enfin, ouais, naturellement, une fois que vous savez ...)

Et pour que cela fonctionne (pour éviter `` attendre est un mot réservé ''), votre fonction de test doit avoir un marqueur asynchrone externe:

it('my test', async () => { ...
Frank Nocke
la source
4
Vous n'êtes pas obligé de l'appeler ainsi await helperFunction(...). Une asyncfonction renvoie une promesse. Vous pouvez simplement gérer la promesse retournée comme vous le feriez avec une fonction non marquée asyncqui renvoie une promesse. Le but est de gérer la promesse, point final. Que la fonction soit asyncou non n'a pas d'importance. awaitest simplement l' une des multiples façons de gérer la promesse.
Louis
1
Vrai. Mais ensuite, je dois investir des lignes sur la capture ... ou mes tests passent comme de faux positifs, et toute promesse qui échoue ne sera pas remarquée (en termes de résultats de testrunner). Donc, attendre me semble moins de lignes et d'efforts. - Bref, oublier attendre était, ce qui a causé ça UnhandledPromiseRejectionWarningpour moi ... donc cette réponse.
Frank Nocke
0

J'ai eu une expérience similaire avec Chai-Webdriver pour Selenium. J'ai ajouté awaità l'assertion et cela a résolu le problème:

Exemple utilisant Cucumberjs:

Then(/I see heading with the text of Tasks/, async function() {
    await chai.expect('h1').dom.to.contain.text('Tasks');
});
Richard
la source
-7

J'ai résolu ce problème après avoir désinstallé webpack (problème react js).

sudo uninstall webpack
Monsieur amusant
la source