Mocha: Erreur: la méthode de résolution est surspécifiée.

Créé le 2 août 2016  ·  84Commentaires  ·  Source: mochajs/mocha

Ce:

before(done => {
    return Promise
        .all([])
        .then(() => Model.insert(...)) // Bookshelf model returning a Bluebird Promise
        .then(() => done())
        .catch(done)
})

entraînera une erreur Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

Les documents disent:

Dans Mocha v3.0.0 et plus récent, renvoyer une promesse et appeler done () entraînera une exception, car il s'agit généralement d'une erreur:

L'appel de modèle se résout avec un Promise.<Object> de l'entrée nouvellement insérée, cependant si j'omets .then(() => done()) alors les délais de test.

confirmed-bug

Commentaire le plus utile

async fonctions (babel) avec done cassent également.

Tous les 84 commentaires

async fonctions (babel) avec done cassent également.

Comme l'indique l'erreur, vous n'êtes pas censé fournir une fonction avec une arité> 0 (ce qui signifie qu'elle accepte un rappel done ), _et_ retourne une promesse.

Le moyen le plus simple de résoudre ce problème serait d'omettre le return , mais comme vous utilisez des promesses, je suggère de vous débarrasser du rappel done place, car cela entraînera une construction beaucoup plus simple :

before(() => Promise.all([]).then(() => Model.insert(...)));

Voici un exemple de async (essentiellement une promesse) et de done qui casse:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

@elado C'est un cas d'utilisation intéressant, bien que du point de vue de moka, c'est la même situation - une fonction qui prend à la fois un rappel done et renvoie une promesse. Je le réécrirais pour qu'il soit entièrement basé sur des promesses:

it('fires change event when calling blah.doSomethingThatFiresChange', async function () {
  const blah = await getBlah()
  return new Promise(resolve => {
    blah.on('change', resolve)
    blah.doSomethingThatFiresChange()
  })
})

... mais je suppose que ce que vous voulez en venir, c'est que dans cet exemple, ce serait mieux si mocha attendait que la promesse soit résolue et que le rappel soit appelé?

Malheureusement, ce combo particulier est le plus souvent un bogue dans le test, c'est pourquoi ce message d'erreur a été ajouté en premier lieu.

... si j'omets .then (() => done ()) alors les délais de test.

Cela ressemble en tout cas à un bug.

@ScottFreeCode Hmm, oui, cela semble être dû au fait que l'erreur "surspécifiée" est émise dans la fonction fournie en tant que rappel done : https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib/ runnable.js # L357 -L373

... mais nous pouvons bien sûr déterminer que nous devons échouer avec cette erreur dès que la fonction est retournée.

@ScottFreeCode Quelle est la solution ici?

Je vais deviner que c'est "attendre que la promesse soit résolue ou rejetée, puis rejetée avec l'erreur" surspécifiée ""?

Si nous allons considérer done plus promesse dans le même test comme une erreur, je ne vois aucune raison de ne pas détecter l'erreur dès qu'une fonction de test qui a pris done renvoie un promesse, comme @papandreou l'a suggéré. Cela n'a pas beaucoup de sens pour moi d'essayer de déterminer quels autres points devraient déclencher l'erreur à moins que nous n'ayons l'intention d'autoriser les promesses et done ensemble dans certains cas.

@ScottFreeCode Je suis d'accord. Alors c'est

  1. Détecter le problème; instanciez le Error mais n'appelez pas done() avec
  2. Attendez que la promesse soit remplie
  3. Rejeter avec le Error

Question bonus: que faire du résultat de l'exécution de la promesse?

Ah, je pense que je comprends - même lorsque nous détectons l'erreur, nous devons laisser le test s'exécuter pour qu'il n'exécute pas encore son code lors du prochain test et, peut-être, pour que nous puissions également signaler le résultat du test.

Un test pourrait-il également finir par appeler done sans résoudre ou rejeter la promesse? Si tel est le cas, c'est un autre cas final que nous devrons gérer.

Mon inclination, re. que faire avec le résultat du test, est que s'il expire (sans avoir _either_ appelé done ou résolu / rejeté la promesse) nous devrions simplement signaler l'utilisation de done avec la promesse (parce que la confusion sur la combinaison pourrait très bien être la raison pour laquelle il n'est jamais arrivé à l'un ou l'autre et a expiré à la place), mais s'il parvient à se terminer par l'un ou l'autre des moyens, alors vraisemblablement le résultat est valide (ou du moins en quelque sorte significatif) et nous devrions le signaler à la fois l'utilisation peut-être erronée de done et promesse ensemble et aussi le résultat du test dans l'espoir que c'est au moins quelque peu utile.

C'est le mieux que je puisse trouver pour le moment, de toute façon, mais j'aurai peut-être plus de perspicacité si je trouve le temps de creuser ce problème.

Eh bien, nous pouvons le faire par étapes. Le premier serait de s'assurer que si une promesse est retournée et qu'un rappel done est donné, alors Mocha se cassera de manière amicale.

Il est _concevable_ que l'on puisse faire quelque chose comme ça (avec Bluebird):

// to make this work, you need to omit `return`
it('should do something async for sure', function(done) {
  return somethingAsync()
    .then(makeAssertion)
    .asCallback(done);
});

Mais c'est juste quelque chose que vous _pourriez_ faire; Je n'ai pas encore déterminé de cas d'utilisation pour cela.

Je pense que je me suis confondu. Je ne suis pas du tout sûr qu'il y ait un bug ici.

Il s’agit davantage d’un problème de type «mauvaise interface utilisateur»

Le délai d'attente de l'appel de done même si une promesse retournée est résolue / rejetée est définitivement un bogue, que nous voulions ou non autoriser de tels tests à utiliser done et promesses ensemble dans le premier endroit. Cela devrait soit utiliser le résultat de la promesse et / ou l'erreur parce que la promesse et done ont été tous les deux utilisés dans le même test, mais pas seulement le temps mort parce que l'un des deux ne s'est jamais terminé lorsque l'autre l'a fait (ce qui est ce qu'il fait actuellement):

it("should not time out", function(done) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve.bind(42), 50)
  })
})

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

Je vais regarder le PR en tout cas ...

Les parties intéressées devraient regarder # 2413.

@briansipple

J'obtiens la même erreur en utilisant plusieurs hooks async beforeEach dans la version 3:

  beforeEach((cb) => connection.db.collection('users').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').insertOne(account1, cb));

N'est-il pas possible de continuer à utiliser des hooks asynchrones de cette manière?

J'ai compris mon problème: mes méthodes à l'intérieur de chaque hook renvoient une fonction promise (je ne le savais pas car je n'utilisais pas de promesses dans ces cas) et évidemment je passe également la fonction cb .

Je ne pense pas que ce soit une bonne idée. Si l'on demande un rappel, je pense qu'ils ont clairement indiqué leurs intentions. L'erreur ici n'est qu'un ennui. Veuillez le supprimer.

Ce n'est pas vraiment une question d'intention. D'après la description de # 1320:

Lorsqu'une fonction de rappel est spécifiée et qu'un objet Promise est
renvoyé, la condition de résolution du Runnable est ambiguë.

Ce n'est pas ambigu, le rappel a été demandé. En quoi est-ce ambigu?

Je suis d'accord avec @RobertWHurst, il n'y a pas d'ambigu ici.

De plus, je pense que ce problème pourrait être quelque chose de "basé sur l'opinion" et les développeurs auront des points de vue différents. Je considère qu'il est très extrémiste de faire un changement radical et de forcer les gens à utiliser de cette façon.

Nous n'avons forcé personne à faire quoi que ce soit. Nous avons publié un majeur, qui, par définition, aura des changements de rupture. Nous n'avons pas rompu le semestre.

Je ne pense pas qu'il veut dire que tu as cassé la moitié, je pense qu'il veut dire que tu as cassé Mocha. Ce changement ne facilite pas les choses pour les développeurs, il applique une opinion.

beforeEach((cb) => connection.db.collection('users').remove({}, cb));
            ^^

^^ Ce n'est pas ambigu. Ce qui est attendu par l'auteur est assez clair. L'auteur a fait tout son possible pour demander un rappel.

En fait, la raison pour laquelle vous obtenez une exception ici est le fait d'éviter au maximum de faire respecter l'opinion. Mocha n'a pas d'opinion sur le fait que la promesse retournée ou le rappel fasse autorité. Vous ne pouvez pas avoir les deux en même temps car cela conduit à des résultats ambigus. Les résultats ambigus dans un cadre de test doivent être considérés comme une erreur. D'où le message d'erreur pour vous aider à en prendre conscience et à faire le choix et le changement correspondant à votre opinion.

Il pourrait être avantageux de réduire le nombre de drames. "vous avez cassé Mocha" n'aide personne. Une augmentation de version majeure semver est explicitement définie comme des changements de rupture qui peuvent vous obliger à ajuster votre code. Vous pouvez rester sur 2.x pour vous laisser le temps d'apporter les modifications nécessaires pour corriger vos tests. C'est une évolution, pas une rupture

@Munter Je ne sais toujours pas à quel point la demande d'un rappel est ambiguë. Si vous demandez un rappel, vous devez l'utiliser. Sinon, c'est une erreur de programmation. Il s'agit d'une action explicite de l'auteur du test.

Si vous ressentez un drame, je veux dire aucun. «Vous avez cassé Mocha» n'est pas censé être hyperbolique. Je pense juste que cela va à l'encontre de la conception du module et casse le contrat original de l'API.

Comme indiqué précédemment, babel async / await ne fonctionne pas bien avec le nouveau mocha @ 3. Exemple:

it('should fill userName and password', async function (done) {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    try {
      assert.equal(userNameVal, 'admin');
      assert.equal(passwordVal, 'changeme');
    } catch (error) {
      done(error);
      return;
    }

    done();
  });

Ce code fonctionne bien avec mocha @ 2 mais pas avec mocha @ 3 car dans await il retourne promesse

Je pense que ce PR est pertinent ici => https://github.com/mochajs/mocha/pull/2413
Plus de complexité pour traiter les cas extrêmes de cette erreur.

@artyomtrityak C'est un excellent exemple où done n'est pas nécessaire.

Les détracteurs ont dit leur mot. Pourtant, les tenants de Mocha ne sont pas d'accord avec l'argument (s) pour annuler ce changement. Eran Hammer a dit (paraphrasé): "En tant que responsable, l'une des choses les plus difficiles que vous puissiez faire est de décevoir ceux qui veulent faire avancer le travail dans leur direction."

Je suis le bienvenu aux solutions de contournement - plus de documentation (par exemple plus d'exemples de cette erreur et comment les corriger), une meilleure messagerie d'erreur - mais je ne suis pas intéressé par le drame, l'impolitesse ou les plaintes. Contribuer à l'une de ces solutions de contournement à Mocha aiderait à transformer un négatif en positif.

Si vous n'aimez pas ce changement et que vous ne pouvez tout simplement pas être constructif à ce sujet, c'est OSS - vous pouvez bifurquer le projet et y annuler les modifications.

@boneskull il se transforme en fonctions asynchrones qui renvoient des promesses, je n'ai pas besoin de terminer mon cas de test lorsque la promesse sera remplie mais je dois faire des vérifications personnalisées autour des résultats. Comme je l'ai dit, ce code fonctionne parfaitement avec mocha @ 2 mais avec mocha @ 3, ce n'est pas le cas. Donc, mon équipe (~ 20ppl) ne peut pas passer au dernier moka à cause de cela.

Actuellement, mocha 2.x donne beaucoup de flexibilité, y a-t-il une raison technique à ce changement?

@artyomtrityak C'est un excellent exemple de ce qui est inutile.

Pouvez-vous donner un exemple à quoi cela devrait ressembler avec la syntaxe babel async/await et sans return new Promise ?

@artyomtrityak Que diriez-vous de

it('should fill userName and password', async function () {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    assert.equal(userNameVal, 'admin');
    assert.equal(passwordVal, 'changeme');
  });

Si vous vous sentez personnellement attaqué, je pense que vous devez reconsidérer à quel point vous êtes émotionnellement investi dans cette conversation. Aucun des commentaires n'a été personnel. Je suis sûr que vous êtes tous formidables, d'autant plus que vous avez donné de votre temps pour aider à maintenir ce projet qui est très apprécié et très louable.

La plupart d'entre vous (les mainteneurs actuellement actifs) ont commencé à travailler sur Mocha vers la mi-2014. Mocha était déjà établi au moment où vous avez commencé à contribuer. C'est juste mon avis, mais je ne pense pas que je suis le seul à penser qu'il ne faut pas apporter de modifications radicales à une bibliothèque établie à moins que cela ne soit justifié. Bien que je puisse imaginer la justification originale de ce changement, cela ne tient pas bien quand on souligne ce qui suit. Demander un rappel communique une intention claire. Les promesses ne sont pas aussi claires car elles ne sont pas demandées, elles sont retournées, ce qui peut arriver indirectement et accidentellement (renvoyées d'une bibliothèque tierce par exemple). En raison de ces différences, les deux modes de rendement ne sont pas égaux, et donc essayer d'utiliser les deux n'est pas vraiment ambigu. Les rappels doivent être écrits dans les arguments de test. Vous ne pouvez pas faire cela avec des promesses, et donc en demandant un rappel, vous avez communiqué vos intentions explicitement. Votre communauté a soulevé ces préoccupations et, au lieu de reconnaître le faux pas, vous redoublez. Il semble que vous envisagez même de forcer les tests à être asynchrones pour vous assurer que cette erreur agit de manière cohérente. Voir => https://github.com/mochajs/mocha/pull/2413. Cela semble être un grand changement pour un message d'erreur protégeant contre une erreur improbable.

Vous avez fait un excellent travail en maintenant cette bibliothèque depuis le départ de @tj , pouvez-vous réfléchir un peu plus à ce changement. Ma préoccupation est que cela pourrait compromettre la bibliothèque.

Tout à fait d'accord avec @RobertWHurst.

La demande de done devrait remplacer le comportement de la promesse retournée. Il est peu probable qu'il demande done quand ce n'est pas nécessaire, et les scénarios d'événements émis dans une fonction async sont un exemple parfait.

De mon commentaire ci-dessus:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

Au fur et à mesure que de plus en plus de gens passeront à ES6 / 7 + async / await , cela deviendra un piège courant lors de l'utilisation de Mocha.

Veuillez reconsidérer ce changement.

@RobertWHurst Vous done est une intention explicite. Une instruction return n'est-elle pas une intention explicite? Les deux sont définis par vous dans votre code. Comment pouvons-nous décider qu'une partie de votre code est intentionnelle et qu'une autre ne l'est pas? Si vous imaginez un monde avant () => foo toute déclaration de retour aurait toujours été explicite. La seule raison pour laquelle vous êtes tous en armes maintenant est que vous avez commencé à utiliser des déclarations de retour implicites, pour ce que je ne peux penser que pour des raisons esthétiques.

Étant donné qu'une grande partie de l'utilisation de Mocha est faite par des débutants qui copient / collent généralement des exemples, qui contiennent très probablement un rappel done , comment géreriez-vous le cas où ce nouvel utilisateur renvoie explicitement une promesse, mais obtient un délai d'expiration? C'est le résultat si le changement dont vous êtes fou est annulé.

Le comportement actuel est beaucoup plus clair sur ce qui ne va pas qu'un simple délai d'attente inattendu

@Munter Avec les fonctions async dans l'image, je pense que la promesse retournée est plus basse sur l'échelle d'explicitation car elle est créée et renvoyée automatiquement, que vous utilisiez ou non await :

it('should work with a async function that could have been sync', async function () {
  assert.ok(true);
});

it('should work with a legitimately async function', async function () {
  assert.equal(await getBlah(), 'blah');
});

it('should work with a fully spelled-out Promise-based test', function () {
  return getBlah().then(function (blah) {
    assert.equal(blah, 'blah');
  });
});

Et puis il y a le controversé:

it('should foo', async function (done) {
  assert.equal('foo', 'foo');
  done();
});

Il est également facile pour cet adolescent async de se faufiler par accident, nous devrions donc considérer (au moins) le premier exemple comme un nouveau type de piège, comme le souligne @elado .

Après un peu de conversation ici => https://github.com/mochajs/mocha/pull/1320, j'ai eu une idée pour une solution alternative au problème. J'ai ajouté un PR pour votre plaisir d'examen ici => https://github.com/mochajs/mocha/pull/2454

:bières:

Vous soutenez que la définition d'un rappel terminé est une intention explicite. Une déclaration de retour n'est-elle pas une intention explicite?

@Munter N'oubliez pas que les expressions de fonction de flèche coffeescript et es6 renvoient _implicitly_, vous pouvez donc faire quelque chose comme

it 'should foo', (done) -> request('/foo').end (err, res, body) -> done()

et pensez que vous êtes en sécurité. Mais ce problème signifie que vous devez transformer ce joli monoplace en quelque chose comme

it 'should foo', (done) ->
  request('/foo').end (err, res, body) -> done()
  return

C'est exactement notre problème avec notre base de code globale dans chaque et mecano . Considère ceci:

it 'should foo', (done) ->
  obj =
    run: ->
      done()
      @
    then: -> "what the hell, i'm not a promise"
  obj.run()

Ce n'est pas complètement spécifique à coffeescript mais le renvoi de valeurs implicites aggrave la situation. Mocha devrait détecter les instances de promesse valides. En outre, peut-être qu'une option pourrait désactiver cette fonctionnalité.

Salut à tous,
Ran dans ce problème avec le code suivant;

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', (done) ->
      chai.request(app).get('/').end((err, res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
        done()
      )
    )
  )
)

Vous avez résolu le problème en respectant la chaîne de promesse et en omettant le rappel done() .

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', ->
      chai.request(app).get('/').then((res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
      )
    )
  )
)

J'espère que cela aidera les autres, qui rencontrent une erreur similaire: sourire:

PS : coeur: Mocha btw: +1:

EDIT Supprimer catch() fonction du commentaire de

@karlbateman Merci. Vous n'avez pas besoin de ce dernier .catch . Une promesse rejetée est comptée comme une erreur par Mocha

@Munter je vois, merci: smiley:

En y réfléchissant un peu plus, j'aime beaucoup le comportement d'attendre à la fois la promesse et le rappel. Y a-t-il eu des progrès dans sa mise en œuvre?

@ light24bulbs Merci pour vos commentaires! Non, afaik personne n'a commencé à travailler là-dessus puisque c'est juste une idée que j'ai lancée là-dedans pour voir les réactions. Je venais juste de vérifier s'il y avait eu des commentaires supplémentaires sur l'idée, des cas où cela ne fonctionne pas, etc.

Existe-t-il une solution de contournement pour cela lors de l'utilisation de babel?

Existe-t-il une solution de contournement pour cela lors de l'utilisation de babel?

J'enroule deux fois:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

@SaschaNaz merci, cela fonctionne en v3.2.0 :)

6 mois plus tard, ce problème freine toujours tous les tests js modernes ...
la honte

@benfavre Merci pour les mots encourageants qui motiveront certainement les volontaires à assumer la responsabilité de mettre en œuvre la solution que vous n'avez pas spécifiée pendant leur temps libre plutôt que de jouer avec leurs enfants

@Munter Pas de soucis, heureux d'avoir pu aider à identifier un problème spécifique auquel j'ai été confronté en tant que nouvel utilisateur de mochajs.
La solution suggérée par

=> L'utilisation exclusive des promesses a fonctionné comme il se doit.

La prochaine fois, je suppose que je devrais juste "+1" comme un morron pour ne pas être insulté gratuitement.
Il n'y avait rien d'insultant dans mon message, de plus ma déclaration reste vraie.

La plupart des gens choisiront simplement un autre cadre ... pour le moment, il est tout simplement cassé avec async / await, sans indication claire nulle part sur le site principal, et sans message d'erreur clair dans le cli.

Bonne année et amusez-vous à jouer avec les enfants.

Refroidissement...

Les discussions ont évolué en "prêter attention" à la fois au rappel done et au Promise retourné: https://github.com/mochajs/mocha/issues/2509

Fermeture. voir # 2509

Au cas où un corps se débat encore ...

Existe-t-il une solution de contournement pour cela lors de l'utilisation de babel?

Maintenant, avec le gestionnaire intégré pour la promesse comme mentionné dans # 2509, nous n'avons pas besoin d'utiliser le wrapping twice hack comme ceci:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

Au lieu de cela, nous pouvons simplement aller avec ceci:

it("should work", async () => {
  await something;
});

Consultez cet article pour plus de détails

@ lo-tp
1.) ce code fonctionne quand il est compilé avec babel?

it("should work", async () => {
  await something;
});

@SerkanSipahi

  • Oui, pour utiliser la syntaxe async/await maintenant, nous avons besoin de l'aide de babel.

Je ne sais pas si je manque quelque chose ... mais j'ai résolu ce problème en n'utilisant aucune déclaration de retour avec mes promesses et en me fiant simplement à done ().

Voici un exemple qui fonctionne pour moi

  describe('STRIPE CHARGES: Get Charges', () => {
    it('should list customer charges', async () => {
      const charges = await Stripe.getChargesList(customerObj);
      charges.data[0].should.have.property('id');
      charges.data[0].amount.should.have.property('id');
    });
  });

J'utilise moka 4 avec chai.assert.

Au début, j'ai essayé d'utiliser le rappel done () comme ça.

it('should throw an error', async function(done) {
  try {
    result = await something('value that causes something() to throw an Error');
    done('failed');
  } catch (e) {
    done();
  }
});

Il a échoué avec le

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both."

Cette erreur est ce qui m'a conduit ici, à cette page. En parcourant ce fil assez long (et j'avoue que je n'ai pas bien compris tous les différends), dois-je comprendre que je ne devrais pas du tout utiliser done () avec des appels asynchrones parce qu'ils sont basés sur la promesse?

Si tel est le cas, comment faire passer le test suivant lorsque l'appel à attendre quelque chose () lève une erreur et c'est ce à quoi je m'attendrais?

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
  } catch (e) {
    // how to indicate that this should happen, if I can't call done() to tell mocha?
  }
});

Quelqu'un pourrait-il m'aider à comprendre ce cas spécifique s'il vous plaît? Dois-je utiliser la bibliothèque d'assertions ou dois-je ajouter quelque chose de plus dans le code ci-dessus?

Merci beaucoup.

La solution de bas niveau et sujette aux erreurs:

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
    throw new Error('Promise unexpectedly fulfilled');
  } catch (e) {
    // Optionally assert something about e...
  }
});

J'utiliserais une bibliothèque d'assertions n'importe quel jour de la semaine:

const expect = require('unexpected');
it('should throw an error', async function () {
    await expect(something(), 'to be rejected with', 'the error message');
});

Ou chai + chai-comme-promis:

const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;

it('should throw an error', async function () {
    await expect(something()).to.be.rejectedWith('argh');
});

Salut les gars, celui-ci fonctionne bien avec asyncawait (omettez simplement 'done ()')

describe('New order from landing', function() {
    describe('check new client function', function () {
        it("must check db and return that random client is new", async function () {
            var result = await handler.checkClient(reqNewClient.body)
                expect(result).to.have.property('newclient').equal(true)
            })
    })
})

résultat:

Nouvel ordre de débarquement
vérifier la nouvelle fonction client
√ doit vérifier db et retourner que le client aléatoire est nouveau
1 passage (9 ms)

@kolykhalov Merci Cela a fonctionné pour moi

Dans mon cas, j'ai enveloppé le bloc async dans un try / catch

it('It should activate a user.', async() => {
        return new Promise((resolve, reject) => {
            try {
                // Get the last activation token from the DB
                let sql = 'select * from activation_tokens order by created_at desc limit 1';
                let res = await DB.query(sql);
                let {token} = res[0];

                server
                    .post('/auth/activate')
                    .send({token})
                    .set('Content-Type', 'application/x-www-form-urlencoded')
                    .expect('Content-Type', /json/)
                    .expect(200)
                    .end((err, res) => {
                        res.body.data.should.contain.keys('message');
                        res.body.data.message.should.equal("Activated");
                        resolve();
                    });
            } catch (e) {
                console.error(e);
            }
        });
    });

Mis à jour pour éviter les vulnérabilités. Maintenant, je dois réécrire 99 tests. FML

Donc, juste pour clarifier le problème, comme certains disent que vous ne devriez pas avoir besoin d'utiliser done et async , voici un exemple où vous voudriez utiliser les deux.

it('should error on fn', async function(done) {
  try {
    await fn();
  } catch (e) {
    // assert some things here
    done()
  }
});

Ne pas utiliser done laissera le test passer si aucune erreur n'est générée. Certains ont suggéré d'utiliser des choses comme expect(await fn).to.throw('blah') , mais parfois vous avez besoin de vérifier plus de propriétés que ce qui va tenir dans une seule ligne.

J'obtenais cette erreur mais je retournais accidentellement une promesse (supertest)

it('Should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

La suppression de la clause 'return' a résolu le problème

Pour quiconque a été rendu fou par ça ...

Cela marche:

it("should work", async () => {
  await something;
});

Cela ne:

it("should work", async (done) => {
  await something;
});

Vous devez supprimer done comme paramètre.

@tamoyal , ouais - comme elado l'a posté le 3 août 2016.

Utiliser async / await renvoie un Promise implicite. L'utilisation du rappel done est pour
Asynchrone basée sur ici . Mocha prend en charge les deux ... mais pas en même temps.
Ceci est documenté et constitue un comportement standard depuis la version Mocha-3.0.

J'obtenais cette erreur mais je retournais accidentellement une promesse (supertest)

it('should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

La suppression de la clause 'return' a résolu le problème

@victorsferreira , semble que cela aurait dû être la solution ...

it('should do something', () => {
  return supertest(server)
    .get(`/api/endpoint`)
    .set('somekey', 'somevalue')
    .expect(200);
});

@tamoyal Oui, c'est ce qui m'a fait trébucher. Il est très peu intuitif d'avoir une bibliothèque tierce qui examine les paramètres d'une fonction que je crée et l'utilise pour prendre une décision sans me dire qu'elle l'a fait.

@mienaikoe , ce scénario exact est explicitement documenté , vous savez ...

@plroebuck deux choses:

1) Il y a une différence entre documenter quelque chose et être l'une des rares bibliothèques de npm à introspecter les paramètres d'une fonction. Je pourrais documenter que je vérifie les antécédents de tous mes amis lorsque je les rencontre pour la première fois, mais c'est toujours étrange et les gens s'en plaindraient encore à moins que j'aie une raison et leur dise explicitement la raison.

2) Il y a une faille dans la documentation:

Dans Mocha v3.0.0 et plus récent, renvoyer une promesse et appeler done () ...

Il ne s'agit pas d'appeler done, il s'agit de _spécifier done comme paramètre_ que vous l'utilisiez ou non.

@mienaikoe

  1. Mocha vérifie le deuxième paramètre de it pour voir si la fonction existe, et (si c'est le cas) son arité pour déterminer si l'utilisateur a ajouté un rappel. Cela se qualifie à peine d'introspection et est couramment utilisé en JavaScript.
  2. Envoyez PR pour corriger la documentation si vous pensez qu'elle est trop vague.

La suppression effectuée en tant que paramètre a fonctionné pour moi!

AVANT:

image

APRÈS (ça marche!)

image

@QauseenMZ , ne voyez aucune différence entre votre code «avant» et «après».

Vous ne rendez pas non plus votre promesse. Ne devrait-il pas ressembler davantage à ce qui suit?

  it('should receive successful RPC response', async () => {
    return await getResponse(unitData)
      .then((res) => {
        expect(res).to.have.status(200);
      });
  });

PS. L'ordre des arguments de rappel de votre nœud est interrompu ... error _always_ passe en premier.

@plroebuck Merci d'avoir mentionné! Je viens de modifier cela.

Je ne gère pas la promesse car la fonction getResponse renvoie un rappel. C'était la seule façon de le faire fonctionner. La fonction getResponse est la suivante:

image

Ici, l'erreur est le deuxième paramètre simplement à cause du retour de la fonction de rappel getResponse. S'il vous plaît laissez-moi savoir ce que vous en pensez. Merci!

Certaines parties semblent un peu illogiques. Pour plus de clarté, quel package request utilisez-vous?
Pourquoi auriez-vous besoin de renvoyer l'objet options (ce que vous avez nommé unitData )?

  • D'où vient obj ?
  • Pourquoi auriez-vous un res.body.error avec un res.statusCode === 200 ?

PS. Veuillez simplement coller le code lui-même plutôt que des images du code ...

Pour quiconque a été rendu fou par ça ...

Cela marche:

it("should work", async () => {
  await something;
});

Cela ne:

it("should work", async (done) => {
  await something;
});

Vous devez supprimer done comme paramètre.

@tamoyal Vous

Cela se brise et entraîne la même erreur:

it('Location Querying Works', async () => {
    await WeatherComponent.pullLocationData();
    Vue.nextTick(()=>{
      expect(WeatherComponent.$el.textContent).contain("Spain")
    })
  })

Pour vous donner une solution rapide à ce problème, enveloppez l'intégralité de votre test dans une promesse et utilisez resolve comme vous le feriez done .

Tournez ceci:

it.only("Works with the database", async (done) => {
    let browser = await puppeteer.launch();
    let query = db.collection('lists').where('ownerId', '==', 'UJIXXwynaCj8oeuWfYa8');
    let ct = 0;
    query.onSnapshot(async querySnapshot => {
        if (ct === 0) {
            await addAPurpose(browser, session, sessionSig); // A function with a bunch of Puppeteer code.
            ct++
        } else {
            expect(querySnapshot.size).to.equal(1);
            expect(querySnapshot.docs[0].get().title).to.equal("Understand Mocha");
            done();
        }
        console.log(`Received query snapshot of size ${querySnapshot.size}`);
    }, err => {
        console.log(`Encountered error: ${err}`);
    });
});

dans ceci:

it.only("Works with the database", async () => {
    const onSnap = new Promise(async (res, rej) => {
        let browser = await puppeteer.launch();
        let query = db.collection('lists').where('ownerId', '==', 'UJIo1gGMueoubgfWfYa8');
        let ct = 0;
        let unsubscribe = query.onSnapshot(async querySnapshot => {
            if (ct === 0) {
                await addAPurpose(browser, session, sessionSig);
                ct++
            } else {
                expect(querySnapshot.size).to.equal(1);
                expect(querySnapshot.docs[0].data().title).to.equal("Evolution");
                // done(); 
                unsubscribe();
                res();
            }
            console.log(`Received query snapshot of size ${querySnapshot.size}`);
        }, err => {
            console.log(`Encountered error: ${err}`);
            rej()
        });
    });
    return onSnap;
});

Et cela fonctionne comme vous le souhaitez.

La nécessité de supprimer done m'a surpris, car le test s'est exécuté jusqu'à ce que done soit appelé. Pour rendre plus évident que done ne doit pas être utilisé avec async, les fonctions async doivent échouer _immédiatement_ si done est passé. Mocha devrait commencer le test par:

  1. Voir si la fonction est asynchrone, et
  2. Détection de l'argument done .

S'ils sont tous les deux présents, il devrait lancer au lieu de laisser mes tests s'exécuter jusqu'à ce que done soit appelé, puis me faire une balle bleue. Ensuite, l'erreur devrait vous suggérer d'encapsuler votre code dans une autre promesse et d'utiliser la résolution comme vous le feriez done .

Je sais que vous pouvez utiliser function.prototype.name === "AsyncFunction" . Alors c'est

if (function.prototype.name === "AsyncFunction && arg1.name === "done") {
  throw new Error("Can't use done with an async function")
}

pour mettre en œuvre cela.

J'étais presque SOL à ce sujet. J'ai besoin d'exécuter du code asynchrone ET il est prévu que le code ne finisse pas de s'exécuter (jamais) donc JE DOIS utiliser done. L'ennuyeux hack-a-round consistait à envelopper mon code de test asynchrone dans une fonction async auto-appelante mais à laisser la fonction it comme fonction de synchronisation.

Solution:

it("It shouldn't be like this", function (done) {
    ( async function(){
        var life = require('InfiniteLife');
        var asyncRes = await life.someCoolstuff();
        assert(asyncRes);
        setTimeout( function(){
            done();
        },letsCallItQuitsNow);
    })();
});

Un exemple de fonctions asynchrones avec rupture terminée.

it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
        let aObj = await admin.createAdmin();
        chai.request(server)
        .post("/authenticate")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: aObj.login,password:aObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            done();
        });
    });

Cas de réussite

it('If the credentials exists in the system it should return the token generated against it.', async () => {
        let adminObj = await admin.createAdmin();
        chai.request(server)
        .post("/auth/login")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: adminObj.login,password:adminObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            // done();
        });
    });

@ Haisum92

Vous n'avez besoin ni de done ni de async dans votre test. Chai http renvoie une promesse. Les tests de Mocha fonctionnent avec du code asynchrone si vous renvoyez une promesse.

it('If the credentials exists in the system it should return the token generated against it.', () => {
  let adminObj = await admin.createAdmin();
  return chai.request(server)
    .post("/auth/login")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: adminObj.login,password:adminObj.password})
    .end((err, res) => {
      res.should.have.status(200);
      res.body.should.be.a("string");
    });
});

@NateZimmer

Votre solution publiée est de chronométrer le test s'il prend trop de temps. Mocha a déjà cette fonctionnalité intégrée.

it("It should be liek this", async function () {
  this.timeout(letsCallItQuitsNow);

  var life = require('InfiniteLife');
  var asyncRes = await life.someCoolstuff();
  assert(asyncRes);
});

Votre exemple ne semble pas non plus échouer au test si le délai d'attente est dépassé, alors soyez prudent en fonction de l'exactitude de celui-ci

`À l'aide des fonctions cloud express / js et Firebase, vérifiez les réponses d'erreur de l'API, puis obtenez un jeton et réessayez. Assurez-vous que si vous utilisez express, vous retournez .json et non .send, fonctionne également les bogues de journalisation avec mocha, alors utilisez plain console.log.
Assurez-vous d'utiliser async et d'émettre complètement

  process.env.NODE_ENV='TESTING';
  const { FIREBASE_UID } = require('dotenv').config()?.parsed;
  const { red, green, white } = require('chalk');
  const chai = require('chai');
  const chaiHttp = require('chai-http');
  const server = require('../lib/api').API;
  const should = chai.should();
  const expect = chai.expect;
  chai.use(chaiHttp);
  const test = chai.request(server).keepOpen();

  const shouldPrint = (details, status) => {
      return white(`details: ${details}, status: ${status}`);
  };

describe('Authorization Middleware Error Check for bad token', () => {

    it(shouldPrint('Bad Request', red(400)), async () => {
        try {
            const res = await test.get('/').send()
            res.should.have.status(400);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Bad request')
            expect(status).to.equal(400)
        } catch (error) { 
            throw error 
        }
    })


    it(shouldPrint('Unauthorized', red(401)), async () => {
        try {
            const res = await test.get('/').set('Authorization', 'Bearer bad123token').send()
            res.should.exist
            res.should.have.status(401);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Unauthorized')
            expect(status).to.equal(401) 
        } catch (error) {
            throw error
        }
    })

})

 describe('Get token and ping', () => {
    let adminToken

    it(shouldPrint( 'Create custom jwt for testing', green(200)), async () => {
        try {
            const res = await test.get(`/createToken/${FIREBASE_UID}`).send()
            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            adminToken = (JSON.parse(res.text)).token
        } catch (error) {
            throw error
        }
    })

    it(shouldPrint('PING', green(200)), async () => {
        try {
            const res = await test.get('/')
                .set('x-tenorders-testing', 'true')
                .set('Authorization', `Bearer ${adminToken}`).send()

            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('PING')
            expect(status).to.equal(200)
        } catch (error) {
            throw error
        }   
    })

})
»

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