Dieser Test:
it('returns the correct value', function(done) {
var returnValue = 5;
aPromise.then(function() {
expect(returnValue).to.equal(42);
done();
});
});
Dieser Test schlägt mit timeout of 2000ms exceeded
anstelle eines Assertionsfehlers fehl. Ich denke, das liegt daran, dass der Aufruf von expect()
einen Fehler auslöst und done()
nie ausgeführt wird, und ich frage mich, ob es eine bessere Möglichkeit gibt, diese Art von Code zu testen.
Wird aPromise
jemals aufgelöst? Wenn nicht, gibt es keine andere Wahl, als eine Auszeit zu werfen.
@NickHeiner Ja, es wird aufgelöst; und dann findet expect()
dass returnValue
nicht equal(42)
und wirft.
@gurdiga woher wissen Sie, dass es
@hallas @NickHeiner Hier ist das laufende Ding: http://jsfiddle.net/gurdiga/p9vmj/.
@gurdiga es scheint mir, dass dein Versprechen seinen eigenen Fehler hat. Versuchen Sie, Ihrem Versprechen ein .catch(done)
hinzuzufügen, und ich denke, es wird wie beabsichtigt funktionieren.
@hallas Wow: _das_ war die Antwort! :) aPromise.finally()
scheint die perfekte Lösung für done
zu sein: es muss auch aufgerufen werden, wenn das Versprechen gelöst ist. ;)
Dankeschön!
Ich fühle mich dumm.
Ich glaube, ich habe es endlich verstanden: Wenn irgendetwas eine der Handlerfunktionen des Promises einwirft, sei es die an .then()
, an .catch()
oder an .finally()
, ist der Fehler von der Promise-Bibliothek verarbeitet. Auf diese Weise sieht der Testläufer nie den wirklichen Fehler, done()
wird nie aufgerufen (weil etwas, bevor es einen Assertion-Fehler ausgelöst hat), und somit ist der Zeitüberschreitungsfehler alles, was Sie vom Testläufer erhalten.
Um aus dem Versprechen herauszukommen, verwende ich setTimeout()
:
it('returns the correct value', function(done) {
var returnValue = 5;
aPromise.then(function() {
setTimeout(function() {
expect(returnValue).to.equal(42);
done();
});
});
});
Ich habe festgestellt, dass dies der einzige Weg ist, um richtige Fehlermeldungen zu erhalten und das Verhalten des Läufers zu testen.
Wenn done
an .catch()
oder .finally()
der Test in jedem Fall als bestanden. Wenn also Assertion-Fehler auftreten, werden Sie diese nie sehen.
Ich bin auf ein ähnliches Problem gestoßen und habe schließlich festgestellt, dass Sie beim Testen asynchroner Funktionen mit Versprechen nicht done verwenden sollten, sondern einfach das Versprechen zurückgeben. Sie sollten dies also ohne Timeout tun können, z.
it('returns the correct value', function() {
var returnValue = 5;
return aPromise.then(function() {
expect(returnValue).to.equal(42);
});
});
Ich denke, dass dieses Problem immer noch besteht. Ich erhalte ein Zeitüberschreitungsproblem, das nicht durch Zurückgeben einer Zusage behoben werden kann, da mein Modul keine Zusagen verwendet.
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();
});
});
Promise
scheint übertrieben.try
/ catch
einzupacken, erscheint ebenfalls übertrieben und führt, was noch wichtiger ist, zu Error: done() called multiple times
.Ideen:
http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/
Was die Empfehlungen des Blog-Posts betrifft, so weiß ich nichts über die Fehlerbehandlung bei asynchronen Funktionen, aber bei einfachen Versprechungen ist die Try-Catch-Empfehlung seltsam: Der ursprüngliche Promise-Versuch ohne Try-Catch war fast korrekt, es musste nur .catch(done)
anstatt den zweiten Parameter für then
als done
. (Zugegeben, da Mocha direkte Unterstützung für Versprechen hat, können Sie das Versprechen auch einfach zurückgeben, aber was es wert ist ...) Das Problem im ersten Versprechen-Beispiel war nicht das Fehlen von Try-Catch, sondern der zweite Handler to then
wird nicht mit Ausnahmen aufgerufen, die vom ersten Handler geworfen werden, während ein folgendes catch
; Ich weiß nicht, was der Grund dafür war, dass Versprechen so gestaltet wurden, aber Versprechen sind so. Wenn es aus irgendeinem Grund mehrere then
s gegeben hätte, wäre außerdem nur ein einziges letztes .catch(done)
erforderlich, was gegenüber Try-Catch innerhalb der Handler (zusätzlich zu den Tatsache, dass .catch(done)
vornherein weniger Boilerplatey ist).
Was Ihre API angeht:
.on("error", function(error) {...})
), dann erreichen sie Mocha nie, es sei denn, Sie hören auf sie und haben die Listener rufen done
mit dem Fehler auf (oder verwenden Sie einfach done
mit dem Listener, wenn dem Listener der Fehler als erster Parameter übergeben wird, zB .on("error", done)
. Vermutlich wäre das auch nur müssen einmal pro Test und nicht einmal pro Event-Handler geschrieben werden, wie .catch(done)
in einem Promise."end"
/ "drain"
Ereignis, um zu überprüfen, ob Boolesche Werte in den anderen Ereignissen gesetzt wurden.Tut mir leid, ich weiß immer noch nicht, wie Ihre API Fehler melden soll.
Ja, bisher habe ich mich nur auf die Timeouts verlassen, um _wenn_ etwas fehlschlägt, und dann manuell zu graben, um herauszufinden, _wie/warum_. Glücklicherweise gehen Dinge selten kaputt, aber das ist keine Entschuldigung für das Fehlen eines besseren Designs (meistens meinerseits).
@stevenvachon : Verzeihen Sie mir im Voraus, aber ich sehe kein unmittelbares Problem mit Ihrem Beispiel. Die in Ihren Event-Listenern gemachten Assertionen sollten von Mocha über das uncaughtException
Mapping behandelt werden (es sei denn, die Event-Emitter-Implementierung fängt Listener-Fehler ab und gibt ein error
Ereignis oder so aus, was dann immer noch einfach ist lösen).
Wenn Ihre Implementierung unter der Haube Versprechen verwendet, aber Ereignisse ausgibt, anstatt das Versprechen offenzulegen, werden Ihre Behauptungen tatsächlich "gefressen". Um dieses Problem zu umgehen, verwende ich unhandledRejection
.
Normalerweise füge ich dies in ein Setup-Skript ein, das vor meinen Tests ausgeführt wird:
process.on('unhandledRejection', function (reason)
{
throw reason;
});
Hinweis: Dies erfordert möglicherweise etwas zusätzliches Ellbogenfett, um in Browsern zu funktionieren.
Ich hoffe, dass Mocha dies wie uncaughtException
da dies ein häufiger Anwendungsfall ist; Nur weil ich Promises verwende, heißt das nicht, dass ich sie an den Anrufer zurückgeben möchte!
Habe das gleiche Problem mit
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.
Das .catch
ist falsch. error => resolve
entspricht function(error) { return resolve }
, was bedeutet, dass resolve
nicht aufgerufen und der Fehler ignoriert wird. Was Sie wollen, ist resolve
mit dem Fehler aufzurufen, der error => resolve(error)
. Natürlich ist die Übergabe einer Callback-Funktion X
, die einfach die Funktion Y
mit denselben Argumenten aufruft, mit denen X
aufgerufen wird, gleichbedeutend mit der Übergabe von Y
als Rückruf, sodass sogar .catch(error => resolve(error))
zu .catch(resolve)
vereinfacht werden könnte. (Sie müssten resolve
nur dann nicht direkt übergeben, wenn Sie es an then
und daher vermeiden müssten, den Ergebnisparameter von then
an resolve
zu verhindern, dass es als Fehler behandelt wird: then(()=>resolve())
anstatt nur .then(resolve)
; aber da Sie den then
Callback für die Assertionen verwenden, kommt dies nicht vor .)
(Außerdem sollte resolve
hier idiomatisch ähnlich wie done
, da es sowohl Erfolg als auch Misserfolg behandelt und beurteilt, was davon abhängt, ob es mit einem Argument aufgerufen wurde oder nicht. Daher der Name in Mochas Fehlermeldung. Aber das kann ein strittiger Punkt sein; lesen Sie weiter.)
In diesem Fall können Sie es jedoch noch weiter vereinfachen, indem Sie einfach das Promise zurückgeben und den Parameter test done überhaupt nicht verwenden, da Mocha darauf wartet, dass das Promise erfolgreich ist oder fehlschlägt (vorausgesetzt, es gibt keinen done-Parameter für die Testfunktion; das Verhalten, falls beide verwendet werden, wird noch ausgehasht):
it('Convert files into base64', () => {
let files = Promise.all(promises);
return files
.then(([actual, expected]) => {
assert.equal(actual, expected, 'Files not are equal');
})
});
@lsphillips das funktioniert bei mir. Vielen Dank!! Ich hoffe, dass Mokka dies auch standardmäßig unterstützt. Ich habe gerade #2640 erstellt.
Ich habe eine Weile gebraucht, um das herauszufinden! Basierend auf den obigen Antworten sind dies die beiden Optionen:
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 Danke für die setTimeout() Idee! Ich hatte ein ähnliches Problem, aber jetzt bekomme ich zumindest richtige Fehlermeldungen!
In meinem Szenario habe ich Nightmare verwendet, um 2end-Tests zu beenden. Die Lösung für mich war .catch(done)
. Sie können done(error)
innerhalb eines anderen Catch-Callbacks aufrufen, wie im folgenden Beispiel.
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 , das funktioniert, aber Sie können stattdessen
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!')
})
})
})
Sie müssen done()
nicht anrufen, wenn Sie ein Versprechen zurückgeben. Siehe meinen Blogbeitrag Using Async/Await with Mocha, Express und Mongoose
Ich habe das folgende Skript verwendet, aber ich habe den gleichen Timeout-Überschreitungsfehler erhalten.
Mein Skript:
describe("getBillingDetail", asynchrone Funktion (){
this.timeout(55000);
it.only("Überprüfe einen gültigen Jobnamen",async-Funktion (erledigt){
this.timeout(55000);
var result = wait url.getBillingDetail('12254785565647858');
Konsole.log (Ergebnis);
behaupten.equal(Ergebnis,wahr);
});
});
Fehler: Timeout von 55000ms überschritten. Stellen Sie bei asynchronen Tests und Hooks sicher, dass "done()" aufgerufen wird; Wenn Sie ein Versprechen zurückgeben, stellen Sie sicher, dass es aufgelöst wird.
Hören Sie auf, in mehreren geschlossenen Ausgaben dasselbe zu buchstabieren. Übergeben Sie keinen abgeschlossenen Rückruf an asynchrone Funktionen. Lesen Sie die Dokumentation zu asynchronen Tests
@Munter Ich habe den abgeschlossenen Rückruf entfernt, aber dieser Fehler tritt erneut auf
Es sieht so aus, als ob Ihr Versprechen nie gelöst wurde.
Hilfreichster Kommentar
Ich bin auf ein ähnliches Problem gestoßen und habe schließlich festgestellt, dass Sie beim Testen asynchroner Funktionen mit Versprechen nicht done verwenden sollten, sondern einfach das Versprechen zurückgeben. Sie sollten dies also ohne Timeout tun können, z.