Mocha: Le test asynchrone échoue avec un délai d'attente au lieu d'une erreur d'assertion

Créé le 5 févr. 2014  ·  25Commentaires  ·  Source: mochajs/mocha

Ce test:

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        expect(returnValue).to.equal(42);
        done();
      });

    });

Ce test échoue avec timeout of 2000ms exceeded au lieu d'une erreur d'assertion. Je suppose que c'est parce que l'appel expect() génère une erreur et que le done() n'est jamais exécuté, et je me demande s'il existe un meilleur moyen de tester ce type de code.

Commentaire le plus utile

J'ai rencontré un problème similaire et j'ai finalement réalisé que vous ne devriez pas utiliser done lorsque vous testez des fonctions asynchrones avec des promesses, renvoyez simplement la promesse. Vous devriez donc pouvoir le faire sans délai d'attente, par exemple :

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Tous les 25 commentaires

Est-ce que aPromise se résout jamais ? Sinon, il n'y a pas beaucoup d'autre choix que de lancer un délai d'attente.

@NickHeiner Oui, cela se résout; puis expect() trouve que returnValue n'est pas equal(42) et lance.

@gurdiga comment savez-vous qu'il se

@hallas @NickHeiner Voici le truc en marche : http://jsfiddle.net/gurdiga/p9vmj/.

@gurdiga, il me semble que votre promesse a sa propre erreur. Essayez d'ajouter un .catch(done) à votre promesse et je pense que cela fonctionnera comme prévu.

@hallas Wow : _that_ était la réponse ! :) aPromise.finally() semble être la solution idéale pour mettre done dans : il doit également être appelé lorsque la promesse est résolue. ;)

Merci!

Je me sens stupide.

Je pense que j'ai enfin compris : quand quelque chose lance l'une des fonctions de gestion de la promesse, que ce soit celle passée à .then() , à .catch() ou à .finally() , l'erreur est géré par la bibliothèque de promesses. De cette façon, le lanceur de test ne voit jamais la vraie erreur, le done() n'est jamais appelé (parce que quelque chose avant qu'il ne lance une erreur d'assertion), et donc l'erreur de délai d'attente est tout ce que vous obtenez du lanceur de test.

Pour sortir de la promesse, j'utilise setTimeout() :

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        setTimeout(function() {
          expect(returnValue).to.equal(42);
          done();
        });
      });
    });

J'ai trouvé que c'était le seul moyen d'obtenir des messages d'erreur appropriés et un comportement de testeur.

Avec done passé à .catch() ou .finally() le test est considéré comme réussi dans tous les cas, donc s'il y a des erreurs d'assertion, vous ne les verrez jamais.

J'ai rencontré un problème similaire et j'ai finalement réalisé que vous ne devriez pas utiliser done lorsque vous testez des fonctions asynchrones avec des promesses, renvoyez simplement la promesse. Vous devriez donc pouvoir le faire sans délai d'attente, par exemple :

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Je pense que ce problème existe toujours. Je rencontre un problème de délai d'attente qui ne peut pas être résolu en retournant une promesse car mon module n'utilise pas de promesses.

it("works", function(done) {
    new Something()
    .on("eventA", function(result) {
        expect(result).to.be.true;
    })
    .on("eventB", function(result) {
        expect(result).to.be.false;
        done();
    });
});
  • Envelopper l'instance dans un Promise semble excessif.
  • Envelopper chaque assertion dans un try / catch semble également excessif et, plus important encore, aboutit à Error: done() called multiple times .

Idées :
http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

En ce qui concerne les recommandations de l'article de blog, je ne connais pas la gestion des erreurs de la fonction asynchrone, mais pour les promesses simples, la recommandation try-catch est étrange : la tentative de promesse d'origine sans try-catch était presque correcte, il suffisait d'utiliser .catch(done) au lieu d'utiliser le deuxième paramètre de then comme done . (D'accord, puisque Mocha a un support direct pour les promesses, vous pouvez aussi simplement retourner la promesse, mais pour ce que ça vaut...) Le problème dans l'exemple de promesse initial n'était pas l'absence de try-catch, c'était que le deuxième gestionnaire to then n'est pas appelé avec des exceptions levées par le premier gestionnaire, alors qu'un catch est ; Je ne sais pas pourquoi les promesses sont conçues de cette façon, mais c'est ainsi que sont les promesses. De plus, s'il y avait eu plusieurs then pour une raison quelconque, un seul .catch(done) final aurait été nécessaire, ce qui est un point de supériorité sur le try-catch à l'intérieur des gestionnaires (en plus du fait que .catch(done) est moins passe-partout pour commencer).

Quant à votre API :

  1. Êtes-vous sûr que les deux événements sont appelés et dans le bon ordre ? S'ils ne l'étaient pas, comment votre test transformerait-il cela en un échec normal ?
  2. Qu'arrive-t-il normalement aux exceptions levées par vos gestionnaires d'événements ? S'ils ne se propagent pas comme ils le font dans le code synchrone et sont plutôt destinés à être écoutés sur l'API (par exemple avec .on("error", function(error) {...}) ), alors ils n'atteindront jamais Mocha à moins que vous ne les écoutiez et que vous ayez l'auditeur appelle done avec l'erreur (ou utilise simplement done avec l'auditeur si l'auditeur reçoit l'erreur comme premier paramètre, par exemple .on("error", done) . Vraisemblablement, cela ne ferait que doivent être écrits une fois par test, plutôt qu'une fois par gestionnaire d'événement, comme .catch(done) dans une promesse.
  1. Oui, et j'utilise un événement "end" / "drain" pour vérifier si les booléens dans les autres événements ont été définis.
  2. Le délai d'attente se produit. J'essaie de trouver une alternative maigre et propre.

Désolé, je ne sais toujours pas comment votre API est censée signaler les erreurs.

Ouais, jusqu'à présent, je me suis juste appuyé sur les délais d'attente pour _quand_ quelque chose échoue, puis j'ai creusé manuellement pour savoir _comment/pourquoi_. Heureusement, les choses se cassent rarement, mais ce n'est pas une excuse pour l'absence d'un meilleur design (de ma part, la plupart du temps).

@stevenvachon : Pardonnez-moi d'avance, mais je ne vois pas de problème immédiat avec votre exemple. Les assertions faites dans vos écouteurs d'événement doivent être gérées par Mocha via le mappage uncaughtException (à moins que l'implémentation de l'émetteur d'événement ne détecte les erreurs de l'écouteur et n'émette un événement error ou quelque chose, ce qui est alors toujours facile à résoudre).

Maintenant, si votre implémentation sous le capot utilise des promesses, mais émet des événements plutôt que d'exposer la promesse, vos affirmations seront en effet "mangées". La façon dont je contourne ce problème est d'utiliser unhandledRejection .

Je mets généralement cela dans un script d'installation qui est exécuté avant mes tests :

process.on('unhandledRejection', function (reason)
{
    throw reason;
});

Remarque : Cela peut nécessiter un peu d'huile de coude supplémentaire pour fonctionner dans les navigateurs.

J'espère voir Mocha prendre en charge cela comme il le fait uncaughtException car il s'agit d'un cas d'utilisation courant ; ce n'est pas parce que j'utilise des promesses que je veux les renvoyer à l'appelant !

Avoir le même problème avec [email protected]

    it('Convert files into base64', (resolve) => {
        let files = Promise.all(promises);

        return files
            .then(([actual, expected]) => {
                assert.equal(actual, expected, 'Files not are equal');
                resolve();
            })
            .catch(error => resolve);
    });
   Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

Le .catch est faux. error => resolve est équivalent à function(error) { return resolve } , ce qui signifie que resolve ne sera pas appelé et l'erreur est ignorée. Ce que vous voulez, c'est appeler resolve avec l'erreur, qui serait error => resolve(error) . Bien sûr, passer une fonction de rappel X qui appelle simplement la fonction Y avec les mêmes arguments que X est équivalent à simplement passer Y comme callback, donc même .catch(error => resolve(error)) pourrait être simplifié en .catch(resolve) . (Vous n'auriez pas besoin de passer directement resolve si vous le passiez à then et, par conséquent, vous deviez éviter de passer le paramètre de résultat de then à resolve pour éviter qu'il ne soit traité comme une erreur : then(()=>resolve()) plutôt que juste .then(resolve) ; mais puisque vous utilisez le rappel then pour les assertions, cela ne s'affiche pas .)

(De plus, idiomatiquement, resolve ici devrait probablement être nommé quelque chose du genre done , car il gère à la fois le succès et l'échec et juge qui est basé sur s'il a été appelé avec un argument ou non. D'où le nom dans le message d'erreur de Mocha. Mais cela peut être un point discutable ; lisez la suite.)

Cependant, dans ce cas, vous pouvez le simplifier encore plus en renvoyant simplement la promesse et en n'utilisant pas du tout le paramètre test done, car Mocha attendra que la promesse réussisse ou échoue pour indiquer le succès ou l'échec du test (à condition qu'il n'y ait pas de paramètre done pour la fonction de test ; le comportement dans le cas où les deux sont utilisés est toujours en cours de hachage):

it('Convert files into base64', () => {
    let files = Promise.all(promises);
    return files
        .then(([actual, expected]) => {
            assert.equal(actual, expected, 'Files not are equal');
        })
});

@lsphillips qui fonctionne pour moi. Merci!! J'espère voir moka prendre en charge cela par défaut également. Je viens de créer #2640.

J'ai mis du temps à comprendre ça ! Sur la base des réponses ci -

npm install --save mocha expect.js q
./node_modules/mocha/bin/mocha test.spec.js

// test.spec.js

var $q = require('q');
var expect = require('expect.js');

describe('tests with done', function(){
    it('returns the correct value from promise', function(done) {
      var returnValue = 5;
      var def = $q.defer();
      def.promise.then((val) => {
        expect(val).to.equal(42);
        done();
      }).catch(done);
      def.resolve(returnValue)
    });
})

describe('tests returning promises', function(){
    it('returns the correct value from promise', function() {
      var returnValue = 5;
      var def = $q.defer();
      def.resolve(returnValue)
      return def.promise.then((val) => {
        expect(val).to.equal(42);
      });
    });
})
  tests with done
    1) returns the correct value from promise

  tests returning promises
    2) returns the correct value from promise


  0 passing (15ms)
  2 failing

  1) tests with done returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:9:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

  2) tests returning promises returns the correct value from promise:
     Error: expected 5 to equal 42
      at Assertion.assert (node_modules/expect.js/index.js:96:13)
      at Assertion.be.Assertion.equal (node_modules/expect.js/index.js:216:10)
      at def.promise.then (tests/test.spec.js:22:24)
      at _fulfilled (node_modules/q/q.js:854:54)
      at self.promiseDispatch.done (node_modules/q/q.js:883:30)
      at Promise.promise.promiseDispatch (node_modules/q/q.js:816:13)
      at node_modules/q/q.js:570:49
      at runSingle (node_modules/q/q.js:137:13)
      at flush (node_modules/q/q.js:125:13)
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickCallback (internal/process/next_tick.js:98:9)

@gurdiga Merci pour l'idée setTimeout() ! J'ai eu un problème similaire, mais maintenant je peux au moins obtenir des messages d'erreur appropriés !

Dans mon scénario, j'ai utilisé Nightmare pour terminer les tests. La solution pour moi était d'utiliser .catch(done) . Vous pouvez appeler done(error) dans un autre catch callback comme dans l'exemple ci-dessous.

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', (done) => {
      nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
          done()
        })
        .catch((error) => {
          screenshot(nightmare)
          done(error)
        })
    })
  })

@itumoraes qui fonctionne, mais vous pouvez le faire à la place :

describe('Clicking in any bad reputation tag', () => {
    it('open the bad reputation modal', () => {
      return nightmare
        .select('#per-page', '50')
        .waitForAjax()
        .click('[data-reputation="bad"]')
        .evaluate(function() {
          return document.querySelector('.vue-modal .ls-modal-title').innerText
        })
        .then(function(title) {
          title.should.equal('Sua segmentação teve uma avaliação ruim!')
        })
    })
  })

Vous n'avez pas besoin d'appeler done() si vous retournez une promesse. Voir mon article de blog Utiliser Async/Await avec Mocha, Express et Mongoose

J'ai utilisé le script ci-dessous mais j'ai eu la même erreur de dépassement de délai d'attente.

Mon script :

describe("getBillingDetail", fonction asynchrone (){
this.timeout(55000);
it.only("vérifier un nom de travail valide",fonction async (fait){
this.timeout(55000);
var result = wait url.getBillingDetail('12254785565647858');
console.log(résultat);
assert.equal(result,true);
});
});

Erreur : dépassement du délai d'expiration de 55 000 ms. Pour les tests et hooks asynchrones, assurez-vous que "done()" est appelé ; si vous retournez une promesse, assurez-vous qu'elle se résout.

Arrêtez d'épeler la même chose dans plusieurs problèmes fermés. Ne transmettez pas un rappel terminé aux fonctions asynchrones. Lire la documentation sur les tests asynchrones

@Munter j'ai supprimé le rappel effectué, mais cette erreur se reproduit

On dirait que ta promesse n'a jamais été résolue.

Cette page vous a été utile?
0 / 5 - 0 notes