Mocha: Fehler: Die Auflösungsmethode ist überbestimmt.

Erstellt am 2. Aug. 2016  ·  84Kommentare  ·  Quelle: mochajs/mocha

Diese:

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

führt zu einem Fehler Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

Ärzte sagen:

In Mocha v3.0.0 und höher führt das Zurückgeben eines Versprechens und das Aufrufen von done () zu einer Ausnahme, da dies im Allgemeinen ein Fehler ist:

Der Modellaufruf wird mit einem Promise.<Object> des neu eingefügten Eintrags aufgelöst. Wenn ich jedoch .then(() => done()) weglasse, werden die Test-Timeouts ausgeführt.

confirmed-bug

Hilfreichster Kommentar

async Funktionen (babel) mit done brechen ebenfalls.

Alle 84 Kommentare

async Funktionen (babel) mit done brechen ebenfalls.

Wie der Fehler besagt, sollten Sie keine Funktion mit einer Arität von> 0 bereitstellen (was bedeutet, dass ein Rückruf von done akzeptiert wird), _und_ ein Versprechen zurückgeben.

Der einfachste Weg, dies zu beheben, besteht darin, das return wegzulassen. Da Sie jedoch Versprechen verwenden, empfehle ich, stattdessen den done -Rückruf zu entfernen, da dies zu einem viel einfacheren Konstrukt führt ::

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

Hier ist ein Beispiel für async (im Wesentlichen ein Versprechen) und done , das bricht:

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

@elado Das ist ein interessanter Anwendungsfall, obwohl es aus Mokkas Sicht dieselbe Situation ist - eine Funktion, die beide einen done Rückruf entgegennimmt und ein Versprechen zurückgibt. Ich würde es so umschreiben, dass es vollständig auf Versprechen basiert:

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

... aber ich denke, was Sie damit meinen, ist, dass es in diesem Beispiel besser wäre, wenn Mokka darauf warten würde, dass sowohl das Versprechen gelöst als auch der Rückruf aufgerufen wird?

Leider ist diese bestimmte Kombination meistens ein Fehler im Test, weshalb diese Fehlermeldung an erster Stelle hinzugefügt wurde.

... wenn ich .then (() => done ()) weglasse, dann die Test-Timeouts.

Dies scheint auf jeden Fall ein Fehler zu sein.

@ScottFreeCode Hmm , ja, es scheint, weil der "überbestimmte" Fehler in der Funktion ausgegeben wird, die als done Rückruf bereitgestellt wird: https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib runnable.js # L357 -L373

... aber wir können natürlich feststellen, dass wir mit diesem Fehler scheitern müssen, sobald die Funktion zurückgekehrt ist.

@ScottFreeCode Was ist das

Ich vermute, es ist "Warten Sie, bis das Versprechen gelöst oder abgelehnt wurde, und lehnen Sie es dann mit dem" überbestimmten "Fehler ab"?

Wenn wir done plus Versprechen im selben Test als Fehler betrachten, sehe ich keinen Grund, den Fehler nicht zu erkennen, sobald eine Testfunktion, die done zurückgibt Versprechen, wie @papandreou vorgeschlagen. Es macht für mich nicht viel Sinn, herauszufinden, welche anderen Punkte den Fehler auslösen sollten, es sei denn, wir beabsichtigen, in einigen Fällen Versprechen und done zusammen zuzulassen.

@ScottFreeCode Ich bin einverstanden. So ist es

  1. Problem erkennen; Instanziieren Sie Error aber rufen Sie damit nicht done() auf
  2. Warten Sie, bis das Versprechen erfüllt ist
  3. Mit Error ablehnen

Bonusfrage: Was tun mit dem Ergebnis der Versprechenerfüllung?

Ah, ich glaube, ich verstehe es - selbst wenn wir den Fehler erkennen, müssen wir den Test laufen lassen, damit er beim nächsten Test nicht noch seinen Code ausführt, und vielleicht können wir auch das Testergebnis melden.

Könnte ein Test auch done aufrufen, ohne das Versprechen zu lösen oder abzulehnen? Wenn ja, ist das ein weiterer Endfall, den wir behandeln müssen.

Meine Neigung, re. Was mit dem Testergebnis zu tun ist, ist, dass wir, wenn das Zeitlimit überschritten wird (ohne dass _either_ done aufgerufen oder das Versprechen gelöst / abgelehnt hat), nur die Verwendung von done mit dem Versprechen melden sollten (weil Verwirrung über die Kombination sehr wohl sein könnte, warum sie nie zu einer der beiden gekommen ist und stattdessen eine Zeitüberschreitung aufgetreten ist), aber wenn es gelingt, mit beiden Mitteln fertig zu werden, ist das Ergebnis vermutlich gültig (oder zumindest irgendwie aussagekräftig) und wir sollten berichten sowohl die möglicherweise fehlerhafte Verwendung von done und das gemeinsame Versprechen als auch das Testergebnis in der Hoffnung, dass es zumindest etwas hilfreich ist.

Das ist sowieso das Beste, was ich mir im Moment einfallen lassen kann, aber ich könnte mehr Einsicht haben, wenn ich die Zeit finde, mich mit diesem Problem zu befassen.

Nun, wir können dies in Phasen tun. Das erste wäre, sicherzustellen, dass Mocha auf freundliche Weise bricht, wenn ein Versprechen zurückgegeben wird und ein Rückruf von done erfolgt.

Es ist denkbar, dass man so etwas machen könnte (mit 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);
});

Aber das ist nur etwas, was Sie tun könnten; Ich habe noch keinen Anwendungsfall dafür festgelegt.

Ich glaube, ich habe mich verwirrt. Ich bin mir nicht sicher, ob es hier überhaupt einen Fehler gibt.

Dies ist eher ein Problem vom Typ "schlechte Benutzeroberfläche"

Das Warten auf den Aufruf von done , obwohl ein zurückgegebenes Versprechen gelöst / abgelehnt wird, ist definitiv ein Fehler, unabhängig davon, ob wir solchen Tests verbieten möchten, done und Versprechen zusammen im ersten zu verwenden Ort. Dies sollte entweder das Ergebnis des Versprechens und / oder den Fehler verwenden, da das Versprechen und done beide im selben Test verwendet wurden, aber nicht nur eine Zeitüberschreitung, da einer der beiden nie abgeschlossen wurde, als der andere dies tat (was ist das, was es derzeit tut):

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

1) sollte keine Zeitüberschreitung haben:
Fehler: Zeitüberschreitung von 2000 ms überschritten. Stellen Sie sicher, dass der Rückruf done () in diesem Test aufgerufen wird.

Ich werde mir auf jeden Fall die PR ansehen ...

Interessenten sollten # 2413 sehen.

@briansipple

Ich erhalte den gleichen Fehler bei Verwendung mehrerer asynchroner beforeEach -Hooks in 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));

Ist es nicht möglich, auf diese Weise weiterhin asynchrone Hooks zu verwenden?

Ich habe mein Problem herausgefunden: Meine Methoden in jedem Hook geben eine promise (das wusste ich nicht, weil ich in diesen Fällen keine Versprechen verwendet habe) und offensichtlich übergebe ich auch die cb -Funktion .

Ich halte das nicht für eine gute Idee. Wenn man einen Rückruf anfordert, haben sie meiner Meinung nach ihre Absichten klargestellt. Der Fehler hier ist nur ein Ärger. Bitte entfernen Sie es.

Dies ist nicht wirklich eine Frage der Absicht. Aus der Beschreibung von # 1320:

Wenn sowohl eine Rückruffunktion als auch ein Promise-Objekt angegeben ist
zurückgegeben, ist die Auflösungsbedingung des Runnable nicht eindeutig.

Es ist nicht mehrdeutig, der Rückruf wurde angefordert. Wie ist das mehrdeutig?

Ich bin damit einverstanden, dass @RobertWHurst hier keine Mehrdeutigkeit gibt.

Abgesehen davon denke ich, dass dieses Problem etwas "meinungsbasiertes" sein könnte und Entwickler unterschiedliche Standpunkte haben werden. Ich halte es für sehr extremistisch, eine bahnbrechende Veränderung vorzunehmen und die Menschen dazu zu zwingen, diese zu verwenden.

Wir haben niemanden gezwungen, irgendetwas zu tun. Wir haben einen Major veröffentlicht, der per Definition bahnbrechende Änderungen haben wird. Wir haben kein Semver gebrochen.

Ich glaube nicht, dass er meint, du hast Semver gebrochen, ich glaube, er meint, du hast Mokka gebrochen. Diese Änderung erleichtert Entwicklern nicht die Arbeit, sondern erzwingt eine Meinung.

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

^^ Das ist nicht mehrdeutig. Es ist ziemlich klar, was der Autor erwartet. Der Autor hat sich sehr bemüht, einen Rückruf anzufordern.

Eigentlich ist der Grund, warum Sie hier eine Ausnahme bekommen, die maximale Vermeidung der Durchsetzung von Meinungen. Mocha hat keine Meinung darüber, ob das zurückgegebene Versprechen oder der Rückruf maßgeblich sind. Sie können nicht beide gleichzeitig haben, da dies zu mehrdeutigen Ergebnissen führt. Mehrdeutige Ergebnisse in einem Testframework sollten als Fehler betrachtet werden. Daher die Fehlermeldung, die Ihnen hilft, sich dessen bewusst zu werden und die Auswahl und Änderung entsprechend Ihrer Meinung zu treffen.

Es könnte von Vorteil sein, die Menge des Dramas zu verringern. "Du hast Mokka gebrochen" hilft niemandem. Eine Erhöhung der Semver-Major-Version wird explizit als Änderung von Änderungen definiert, bei denen Sie möglicherweise Ihren Code anpassen müssen. Sie können auf 2.x bleiben, um Zeit zu haben, die Änderungen vorzunehmen, um Ihre Tests zu korrigieren. Dies ist eine Evolution, kein Bruch

@Munter Ich bin immer noch ratlos darüber, wie zweideutig das Anfordern eines Rückrufs ist. Wenn Sie um einen Rückruf bitten, wird von Ihnen erwartet, dass Sie ihn verwenden. Ansonsten ist es ein Programmierfehler. Dies ist eine explizite Aktion des Testautors.

Wenn Sie ein Drama spüren, meine ich keines. "Du hast Mokka gebrochen" soll nicht hyperbolisch sein. Ich denke nur, dass dies gegen das Design des Moduls verstößt und den ursprünglichen API-Vertrag bricht.

Wie meintoned vor babel async / await funktioniert nicht gut mit neuem mokka @ 3. Beispiel:

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

Dieser Code funktioniert gut mit Mokka @ 2 , aber nicht mit Mokka @ 3, da er in await Versprechen zurückgibt

Ich denke, diese PR ist hier relevant => https://github.com/mochajs/mocha/pull/2413
Mehr Komplexität bei der Behandlung der Randfälle dieses Fehlers.

@artyomtrityak Das ist ein großartiges Beispiel dafür, wo done unnötig ist.

Die Kritiker haben ihr Stück gesagt. Die Erhalter von Mocha sind jedoch nicht mit den Argumenten einverstanden, diese Änderung rückgängig zu machen. Eran Hammer sagte (umschrieben): "Als Betreuer ist es eines der schwierigsten Dinge, diejenigen zu enttäuschen, die die Arbeit in ihre Richtung bewegen wollen."

Ich freue mich über Problemumgehungen - mehr Dokumentation (z. B. mehr Beispiele für diesen Fehler und wie man sie behebt), bessere Fehlermeldungen - aber ich bin nicht an Drama, Unhöflichkeit oder Beschwerden interessiert. Wenn Sie eine dieser Problemumgehungen zu Mocha beitragen, können Sie aus einem Negativen ein Positives machen.

Wenn Ihnen diese Änderung nicht gefällt und Sie sie einfach nicht konstruktiv gestalten können, handelt es sich um OSS. Sie können das Projekt verzweigen und die Änderungen dort rückgängig machen.

@boneskull verwandelt sich in asynchrone Funktionen, die Versprechen zurückgeben. Ich muss meinen Testfall nicht beenden, wenn das Versprechen erfüllt wird, sondern einige benutzerdefinierte Überprüfungen der Ergebnisse durchführen. Wie gesagt, dieser Code funktioniert einwandfrei mit Mokka @ 2, aber mit Mokka @ 3 nicht. Mein Team (~ 20ppl) kann aus diesem Grund nicht zum neuesten Mokka wechseln.

Derzeit bietet Mokka 2.x viel Flexibilität. Gibt es einen technischen Grund für diese Änderung?

@artyomtrityak Das ist ein großartiges Beispiel dafür, wo es nicht nötig ist.

Können Sie bitte ein Beispiel machen, wie dies mit der Syntax babel async/await und ohne return new Promise aussehen sollte?

@artyomtrityak Wie wäre es, Versprechen vollständig

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

Wenn Sie sich persönlich angegriffen fühlen, müssen Sie sich überlegen, wie emotional Sie in dieses Gespräch investiert sind. Keiner der Kommentare war persönlich. Ich bin sicher, dass Sie alle großartig sind, besonders wenn Sie Ihre Zeit gespendet haben, um dieses Projekt aufrechtzuerhalten, das sehr geschätzt und sehr lobenswert ist.

Die meisten von Ihnen (die derzeit aktiven Betreuer) haben Mitte 2014 begonnen, an Mokka zu arbeiten. Mocha war bereits etabliert, als ihr angefangen habt, einen Beitrag zu leisten. Es ist nur meine Meinung, aber ich glaube nicht, dass ich allein bin, wenn ich denke, dass man keine Änderungen an einer etablierten Bibliothek vornehmen sollte, es sei denn, dies ist gerechtfertigt. Obwohl ich mir die ursprüngliche Rechtfertigung für diese Änderung vorstellen kann, steht sie nicht gut, wenn man auf Folgendes hinweist. Das Bitten um einen Rückruf vermittelt eine klare Absicht. Versprechen sind nicht so klar, weil sie nicht angefordert werden, sondern zurückgegeben werden, was indirekt und versehentlich geschehen kann (z. B. aus einer Bibliothek eines Drittanbieters). Aufgrund dieser Unterschiede sind die beiden Arten des Nachgebens nicht gleich, und daher ist der Versuch, beide zu verwenden, nicht wirklich mehrdeutig. Rückrufe müssen in die Testargumente geschrieben werden. Sie können dies nicht mit Versprechungen tun. Wenn Sie also um einen Rückruf bitten, haben Sie Ihre Absichten explizit mitgeteilt. Ihre Gemeinde hat diese Bedenken geäußert, und anstatt den Fehltritt anzuerkennen, verdoppeln Sie sich. Anscheinend erwägen Sie sogar, Tests als asynchron zu erzwingen, um sicherzustellen, dass dieser Fehler konsistent auftritt. Siehe => https://github.com/mochajs/mocha/pull/2413. Scheint eine große Änderung für eine Fehlermeldung zu sein, die vor einem unwahrscheinlichen Fehler schützt.

Ihr habt seit der Abreise von @tj großartige Arbeit geleistet, um diese Bibliothek zu pflegen. ? Ich befürchte, dies könnte die Bibliothek gefährden.

Stimme voll und ganz @RobertWHurst zu.

Das Anfordern von done sollte das zurückgegebene Versprechen-Verhalten überschreiben. Es ist unwahrscheinlich, dass done wenn es nicht benötigt wird, und Szenarien von ausgegebenen Ereignissen in einer async -Funktion sind ein perfektes Beispiel.

Aus meinem Kommentar oben:

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

Je mehr Leute zu ES6 / 7 + async / await wechseln, desto häufiger wird dies bei der Verwendung von Mokka.

Bitte überdenken Sie diese Änderung.

@RobertWHurst Sie argumentieren, dass das Definieren eines done Rückrufs eine explizite Absicht ist. Ist eine return -Anweisung keine explizite Absicht? Beide werden von Ihnen in Ihrem Code definiert. Wie können wir entscheiden, dass ein Teil Ihres Codes beabsichtigt ist und ein anderer nicht? Wenn Sie sich eine Welt vor () => foo vorstellen, wäre jede Rückgabeerklärung immer explizit gewesen. Der einzige Grund, warum Sie jetzt alle in den Armen sind, ist, dass Sie angefangen haben, implizite Rückgabeanweisungen zu verwenden, was ich nur für ästhetische Gründe halten kann.

Wie würden Sie mit dem Fall umgehen, in dem dieser neue Benutzer ausdrücklich ein Versprechen zurückgibt, aber eine Zeitüberschreitung erhält, da Anfänger häufig Mokka verwenden und normalerweise Beispiele kopieren / einfügen, die sehr wahrscheinlich einen Rückruf von done enthalten? Dies ist das Ergebnis, wenn die Änderung, auf die Sie verrückt sind, rückgängig gemacht wird.

Das aktuelle Verhalten zeigt viel deutlicher, was falsch ist, als nur eine unerwartete Zeitüberschreitung

@Munter Mit async Funktionen im Bild denke ich, dass das zurückgegebene Versprechen auf der expliziten Skala niedriger ist, da es automatisch erstellt und zurückgegeben wird, unabhängig davon, ob Sie await oder nicht:

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

Und dann ist da noch der umstrittene:

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

Es ist auch leicht für diesen winzigen async , sich versehentlich einzuschleichen , also sollten wir uns (zumindest) das erste Beispiel als eine neue Art von @elado betont .

Nach einigem Gespräch hier => https://github.com/mochajs/mocha/pull/1320 hatte ich eine Idee für eine alternative Lösung des Problems. Ich habe hier eine PR für Ihr Überprüfungsvergnügen hinzugefügt => https://github.com/mochajs/mocha/pull/2454

:Biere:

Sie argumentieren, dass das Definieren eines erledigten Rückrufs eine explizite Absicht ist. Ist eine Rückgabeerklärung keine explizite Absicht?

@Munter Vergessen Sie nicht, dass Coffeescript- und es6-Pfeilfunktionsausdrücke _implicitly_ zurückgeben, damit Sie so etwas tun können

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

und denke, du bist in Sicherheit. Aber dieses Problem bedeutet, dass Sie diesen schönen Einzeiler in so etwas verwandeln müssen

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

Dies ist genau unser Problem mit unserer gesamten Codebasis in jedem und Mecano . Bedenken Sie:

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

Dies ist nicht vollständig spezifisch für Coffeescript, aber die Rückgabe impliziter Werte macht es am schlimmsten. Mokka sollte gültige Versprechungsinstanzen erkennen. Möglicherweise kann eine Option diese Funktion auch deaktivieren.

Hallo zusammen,
Ist auf dieses Problem mit dem folgenden Code gestoßen;

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()
      )
    )
  )
)

Dies wurde gelöst, indem die Versprechen-Kette respektiert und der Rückruf done() weggelassen wurde.

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')
      )
    )
  )
)

Ich hoffe das hilft anderen, die auf einen ähnlichen Fehler stoßen: smile:

PS : Herz: Mokka übrigens: +1:

BEARBEITEN Entfernen Sie catch() basierend auf dem Kommentar von @Munter .

@ Karlbateman Danke. Das letzte .catch brauchst du allerdings nicht. Ein abgelehntes Versprechen wird von Mocha als Fehler gewertet

@Munter Ich

Wenn ich ein bisschen mehr darüber nachdenke, mag ich das Verhalten, sowohl auf das Versprechen als auch auf den Rückruf zu warten. Wurden Fortschritte bei der Umsetzung erzielt?

@ light24bulbs Danke für das Feedback! Nein, afaik, niemand hat angefangen daran zu arbeiten, da es nur eine Idee ist, die ich hineingeworfen habe, um die Reaktionen zu sehen. Ich habe gerade geprüft, ob es zusätzliches Feedback zu der Idee gegeben hat, in welchen Fällen es nicht funktioniert usw.

Gibt es eine Problemumgehung dafür, wenn Sie babel verwenden?

Gibt es eine Problemumgehung dafür, wenn Sie babel verwenden?

Ich wickle zweimal:

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

@SaschaNaz danke, das funktioniert in v3.2.0 :)

6 Monate später bremst dieses Problem immer noch alle modernen js-Tests ...
Schande

@benfavre Vielen Dank für die ermutigenden Worte, die Freiwillige auf jeden Fall dazu motivieren, die Verantwortung für die Implementierung einer Lösung zu übernehmen, die Sie in ihrer Freizeit nicht angegeben haben, anstatt mit ihren Kindern zu spielen

@Munter Keine Sorge, ich bin froh, dass ich helfen konnte, ein bestimmtes Problem zu identifizieren, mit dem ich als neuer Benutzer von Mochajs konfrontiert war.
@SaschaNaz schlug vor, die Lösung des zweimaligen Einwickelns nicht zu

=> Versprechen ausschließlich zu verwenden hat funktioniert wie es sollte.

Das nächste Mal sollte ich einfach "+1" wie ein Morron sein, damit ich nicht kostenlos beleidigt werde.
In meiner Nachricht war nichts Beleidigendes, außerdem bleibt meine Aussage wahr.

Die meisten Leute werden einfach ein anderes Framework wählen ... ab sofort ist es einfach mit async / await kaputt, ohne eindeutige Hinweise irgendwo auf der Hauptseite und ohne eindeutige Fehlermeldung in der CLI.

Frohes neues Jahr und viel Spaß beim Spielen mit Kindern.

Ausruhen...

Die Diskussionen entwickelten sich zu einer "Aufmerksamkeit" sowohl für den done -Rückruf als auch für den zurückgegebenen Promise : https://github.com/mochajs/mocha/issues/2509

Schließen. siehe # 2509

Für den Fall, dass ein Körper immer noch Probleme hat ...

Gibt es eine Problemumgehung dafür, wenn Sie babel verwenden?

Mit dem eingebauten Handler für Versprechen, wie in # 2509 erwähnt, müssen wir das wrapping twice hack wie folgt verwenden:

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

Stattdessen können wir einfach so weitermachen:

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

Überprüfen Sie diesen Beitrag für weitere Details

@ lo-tp
1.) Dieser Code funktioniert, wenn er mit Babel kompiliert wurde.

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

@SerkanSipahi

  • Ja, um jetzt die Syntax async/await , benötigen wir die Hilfe von babel.

Ich bin mir nicht sicher, ob mir etwas fehlt ... aber ich habe dieses Problem gelöst, indem ich keine return-Anweisungen für meine Versprechen verwendet habe und mich nur auf done () verlassen habe.

Hier ist ein Beispiel, das für mich arbeitet

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

Ich habe Mokka 4 mit chai.assert verwendet.

Zuerst habe ich versucht, den Rückruf done () so zu verwenden.

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

Es ist mit dem gescheitert

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

Dieser Fehler hat mich hierher zu dieser Seite geführt. Wenn ich mich durch diesen ziemlich langen Thread wate (und ich gebe zu, dass ich nicht alle Streitigkeiten vollständig verstanden habe), muss ich dann verstehen, dass ich done () überhaupt nicht mit asynchronen Aufrufen verwenden sollte, weil sie auf Promised basieren?

Wenn ja, wie kann ich den folgenden Test bestehen, wenn der Aufruf zum Warten auf etwas () einen Fehler auslöst und ich dies erwarten würde?

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

Könnte mir bitte jemand helfen, diesen speziellen Fall zu verstehen? Sollte ich die Assertionsbibliothek verwenden oder muss ich zusätzlich etwas in den obigen Code einfügen?

Danke vielmals.

Die niedrige und fehleranfällige Lösung:

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

Ich würde jeden Tag der Woche eine Assertionsbibliothek verwenden:

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

Oder Chai + Chai wie versprochen:

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

Hallo Leute, dieser funktioniert gut mit asyncawait (lass 'done ()' einfach weg)

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

Ergebnis:

Neue Bestellung von der Landung
Überprüfen Sie die neue Client-Funktion
√ muss db überprüfen und zurückgeben, dass der zufällige Client neu ist
1 Durchgang (9 ms)

@ kolykhalov Danke Es hat bei mir funktioniert

In meinem Fall habe ich den asynchronen Block in einen Versuch / Fang eingeschlossen

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

Aktualisiert, um Schwachstellen zu vermeiden. Jetzt muss ich 99 Tests neu schreiben. FML

Um zu verdeutlichen, wo das Problem liegt, da einige sagen, dass Sie done und async , finden Sie hier ein Beispiel, in dem Sie beide verwenden möchten.

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

Wenn Sie done wird der Test bestanden, wenn kein Fehler ausgegeben wird. Einige haben vorgeschlagen, Dinge wie expect(await fn).to.throw('blah') , aber manchmal müssen Sie mehr Eigenschaften überprüfen, als in einen Einzeiler passen.

Ich habe diesen Fehler erhalten, aber ich habe versehentlich ein Versprechen zurückgegeben (Supertest).

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

Das Entfernen der 'return'-Klausel löste das Problem

Für alle anderen, die davon verrückt geworden sind ...

Das funktioniert:

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

Das tut nicht:

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

Sie müssen done als Parameter entfernen.

@tamoyal , yeah - wie elado am 3. August 2016

Die Verwendung von async / await gibt ein implizites Promise . Die Verwendung des Rückrufs done ist für
CPS- basierte Asynchronität, wie hier gezeigt. Mokka unterstützt beide ... aber nicht gleichzeitig.
Dies ist dokumentiert und seit der Veröffentlichung von Mocha-3.0 Standard.

Ich habe diesen Fehler erhalten, aber ich habe versehentlich ein Versprechen zurückgegeben (Supertest).

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

Das Entfernen der 'return'-Klausel löste das Problem

@ Victorferferreira , scheint, das hätte die Lösung sein sollen ...

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

@tamoyal Ja , das war es, was mich auslöste. Es ist sehr unintuitiv, wenn eine Bibliothek eines Drittanbieters die Parameter einer von mir erstellten Funktion untersucht und diese verwendet, um eine Entscheidung zu treffen, ohne mir dies mitzuteilen.

@mienaikoe , genau dieses Szenario ist explizit dokumentiert , weißt du ...

@plroebuck zwei Dinge:

1) Es gibt einen Unterschied zwischen der Dokumentation von etwas und der Tatsache, dass Sie eine der wenigen Bibliotheken in npm sind, die die Parameter einer Funktion überprüfen. Ich könnte dokumentieren, dass ich alle meine Freunde einer Hintergrundüberprüfung unterziehe, wenn ich sie zum ersten Mal treffe, aber es ist immer noch seltsam und die Leute würden sich immer noch darüber beschweren, wenn ich keinen Grund hätte und ihnen ausdrücklich den Grund mitteilen würde.

2) Die Dokumentation enthält einen Fehler:

In Mocha v3.0.0 und höher wird ein Versprechen zurückgegeben und done () aufgerufen ...

Es geht nicht darum, fertig aufzurufen, sondern done als Parameter anzugeben, unabhängig davon, ob Sie es verwenden oder nicht.

@mienaikoe

  1. Mocha überprüft den zweiten Parameter von it , um festzustellen, ob eine Funktion vorhanden ist, und (falls ja) seine Arität, um festzustellen, ob der Benutzer einen Rückruf hinzugefügt hat. Dies ist kaum als Selbstbeobachtung zu qualifizieren und wird häufig in JavaScript verwendet.
  2. Senden Sie PR, um die Dokumentation zu korrigieren, wenn Sie der Meinung sind, dass sie zu vage ist.

Das Entfernen als Parameter hat bei mir funktioniert!

VOR:

image

NACH (funktioniert!)

image

@QauseenMZ , sehen Sie keinen Unterschied zwischen Ihrem "Vorher" - und "

Sie geben Ihr Versprechen auch nicht zurück. Sollte es nicht mehr wie folgt sein?

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

PS. Die Reihenfolge Ihrer Node-Callback-Argumente ist fehlerhaft ... error _ steht immer an erster Stelle.

@plroebuck Danke fürs Erwähnen! Ich habe das gerade bearbeitet.

Ich behandle das Versprechen nicht, weil die Funktion getResponse einen Rückruf zurückgibt. Nur so konnte ich es zum Laufen bringen. Die Funktion getResponse lautet wie folgt:

image

Hier ist der Fehler der zweite Parameter, nur weil der Rückruf getResponse zurückkehrt. Bitte teilen Sie mir Ihre Gedanken dazu mit. Vielen Dank!

Einige Teile scheinen irgendwie unlogisch. Aus Gründen der Klarheit, welches request -Paket verwenden Sie?
Warum sollten Sie das Objekt options (wie Sie unitData )?

  • Woher kommt obj ?
  • Warum sollten Sie ein res.body.error mit einem res.statusCode === 200 ?

PS. Bitte fügen Sie einfach den Code selbst ein und nicht die Bilder des Codes ...

Für alle anderen, die davon verrückt geworden sind ...

Das funktioniert:

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

Das tut nicht:

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

Sie müssen done als Parameter entfernen.

@tamoyal Du hast mein Leben gerettet <3

Dies bricht ab und führt zu demselben Fehler:

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

Um eine schnelle Lösung für dieses Problem zu finden, verpacken Sie Ihren gesamten Test in ein Versprechen und verwenden Sie resolve wie done .

Drehen Sie dies:

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

das sehr gut finden:

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

Und es funktioniert wie Sie wollen.

Die Notwendigkeit, done zu entfernen, überraschte mich, da der Test bis zum Abschluss ausgeführt wurde. Um klarer zu machen, dass done nicht mit Async verwendet werden sollte, sollten asynchrone Funktionen sofort fehlschlagen, wenn done übergeben wird. Mokka sollte den Test starten durch:

  1. Überprüfen, ob die Funktion asynchron ist, und
  2. Erkennen des Arguments done .

Wenn beide anwesend sind, sollte es werfen, anstatt meine Tests laufen zu lassen, bis done aufgerufen wird, und mich dann blau zusammenballen. Dann sollte der Fehler darauf hindeuten, dass Sie Ihren Code in ein anderes Versprechen einschließen und die Auflösung so verwenden, als würden Sie done .

Ich weiß, dass Sie function.prototype.name === "AsyncFunction" . Dann ist es

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

um dies umzusetzen.

Ich war fast SOL in dieser it -Funktion als Synchronisierungsfunktion zu belassen.

Lösung:

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

Ein Beispiel für asynchrone Funktionen mit fertigem Brechen.

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

Erfolgsfall

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

Sie benötigen weder done noch async in Ihrem Test. Chai http gibt ein Versprechen zurück. Mochas Tests funktionieren mit asynchronem Code, wenn Sie ein Versprechen zurückgeben.

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

Ihre veröffentlichte Lösung besteht darin, den Test zu beenden, wenn er zu lange dauert. Mocha hat diese Funktionalität bereits eingebaut.

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

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

Ihr Beispiel scheint den Test auch nicht zu bestehen, wenn das Zeitlimit überschritten wird. Seien Sie also vorsichtig, je nach Richtigkeit

`Überprüfen Sie mithilfe von Express / js und Firebase-Cloud-Funktionen die API-Fehlerantworten, holen Sie sich ein Token und versuchen Sie es erneut. Stellen Sie sicher, dass Sie bei Verwendung von Express .json und nicht .send zurückgeben. Außerdem funktionieren Logger-Fehler mit Mokka. Verwenden Sie daher plain console.log.
Stellen Sie sicher, dass Sie Async verwenden und die Ausgabe vollständig ausführen

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

})
`

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen