Ich führe einige asynchrone Tests in Mocha mit dem Browser Runner aus und versuche, Chais Assertion Style Assertions zu verwenden:
window.expect = chai.expect;
describe('my test', function() {
it('should do something', function (done) {
setTimeout(function () {
expect(true).to.equal(false);
}, 100);
}
}
Dies gibt mir nicht die normale fehlgeschlagene Bestätigungsnachricht, sondern ich erhalte:
Error: the string "Uncaught AssertionError: expected true to equal false" was thrown, throw an Error :)
at Runner.fail (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3475:11)
at Runner.uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3748:8)
at uncaught (http://localhost:8000/tests/integration/mocha/vendor/mocha.js:3778:10)
Es fängt also offensichtlich den Fehler auf, es zeigt ihn einfach nicht richtig an. Irgendwelche Ideen, wie das geht? Ich denke, ich könnte mit einem Fehlerobjekt einfach "erledigt" nennen, aber dann verliere ich die Eleganz von etwas wie Chai und es wird sehr klobig ...
javascript
unit-testing
mocha
Thomas Parslow
quelle
quelle
chai-as-promised
Plugin ansehen ...Antworten:
Ihr asynchroner Test generiert bei fehlgeschlagenen
expect()
Aktionen eine Ausnahme, die nicht erfasst werden kann,it()
da die Ausnahme außerhalb desit()
Gültigkeitsbereichs des Bereichs ausgelöst wird .Die erfasste Ausnahme, die angezeigt wird, wird mit
process.on('uncaughtException')
unter Knoten oderwindow.onerror()
im Browser erfasst .Um dieses Problem zu beheben, müssen Sie die Ausnahme innerhalb der von aufgerufenen asynchronen Funktion
setTimeout()
erfassen, umdone()
mit der Ausnahme als erstem Parameter aufzurufen . Sie müssen auchdone()
ohne Parameter aufrufen , um den Erfolg anzuzeigen. Andernfalls meldet Mokka einen Timeout-Fehler, da Ihre Testfunktion niemals signalisiert hätte, dass dies durchgeführt wurde:window.expect = chai.expect; describe( 'my test', function() { it( 'should do something', function ( done ) { // done() is provided by it() to indicate asynchronous completion // call done() with no parameter to indicate that it() is done() and successful // or with an error to indicate that it() failed setTimeout( function () { // Called from the event loop, not it() // So only the event loop could capture uncaught exceptions from here try { expect( true ).to.equal( false ); done(); // success: call done with no parameter to indicate that it() is done() } catch( e ) { done( e ); // failure: call done with an error Object to indicate that it() failed } }, 100 ); // returns immediately after setting timeout // so it() can no longer catch exception happening asynchronously } }
Dies in all Ihren Testfällen zu tun, ist ärgerlich und nicht TROCKEN. Daher möchten Sie möglicherweise eine Funktion bereitstellen, die dies für Sie erledigt. Nennen wir diese Funktion
check()
:function check( done, f ) { try { f(); done(); } catch( e ) { done( e ); } }
Mit können
check()
Sie jetzt Ihre asynchronen Tests wie folgt umschreiben:window.expect = chai.expect; describe( 'my test', function() { it( 'should do something', function( done ) { setTimeout( function () { check( done, function() { expect( true ).to.equal( false ); } ); }, 100 ); } }
quelle
Hier sind meine bestandenen Tests für ES6 / ES2015-Versprechen und ES7 / ES2016 async / await. Hoffe, dies bietet eine schöne aktualisierte Antwort für alle, die dieses Thema erforschen:
import { expect } from 'chai' describe('Mocha', () => { it('works synchronously', () => { expect(true).to.equal(true) }) it('works ansyncronously', done => { setTimeout(() => { expect(true).to.equal(true) done() }, 4) }) it('throws errors synchronously', () => { return true throw new Error('it works') }) it('throws errors ansyncronously', done => { setTimeout(() => { return done() done(new Error('it works')) }, 4) }) it('uses promises', () => { var testPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello') }, 4) }) testPromise.then(result => { expect(result).to.equal('Hello') }, reason => { throw new Error(reason) }) }) it('uses es7 async/await', async (done) => { const testPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello') }, 4) }) try { const result = await testPromise expect(result).to.equal('Hello') done() } catch(err) { done(err) } }) /* * Higher-order function for use with async/await (last test) */ const mochaAsync = fn => { return async (done) => { try { await fn() done() } catch (err) { done(err) } } } it('uses a higher order function wrap around async', mochaAsync(async () => { const testPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello') }, 4) }) expect(await testPromise).to.equal('Hello') })) })
quelle
Wenn Sie versprochen mögen, versuchen Sie Chai als versprochen + Q , die so etwas erlauben:
doSomethingAsync().should.eventually.equal("foo").notify(done);
quelle
Ich habe dasselbe in der Mokka-Mailingliste gefragt. Sie sagten mir im Grunde Folgendes: um einen asynchronen Test mit Mocha und Chai zu schreiben:
if (err) done(err);
done()
.Es löste mein Problem und änderte keine einzige Zeile meines Codes dazwischen (unter anderem Chai-Erwartungen). Das
setTimout
ist nicht die Art und Weise Asynchron - Tests zu tun.Hier ist der Link zur Diskussion in der Mailingliste .
quelle
setTimeout
in dieser Frage als Beispiel verwendete Funktion weist keinen Fehler im Rückruf auf.Ich habe ein Paket veröffentlicht, das dieses Problem behebt.
Installieren Sie zuerst das
check-chai
Paket:npm install --save check-chai
Verwenden Sie
chai.use(checkChai);
dann in Ihren Tests diechai.check
Hilfsfunktion wie folgt:var chai = require('chai'); var dirtyChai = require('dirty-chai'); var checkChai = require('check-chai'); var expect = chai.expect; chai.use(dirtyChai); chai.use(checkChai); describe('test', function() { it('should do something', function(done) { // imagine you have some API call here // and it returns (err, res, body) var err = null; var res = {}; var body = {}; chai.check(done, function() { expect(err).to.be.a('null'); expect(res).to.be.an('object'); expect(body).to.be.an('object'); }); }); });
Per Gibt es eine Möglichkeit, Chai mit asynchronen Mokka-Tests zum Laufen zu bringen? Ich habe dies als NPM-Paket veröffentlicht.
Weitere Informationen finden Sie unter https://github.com/niftylettuce/check-chai .
quelle
Versuchen Sie es mit chaiAsPromised! Abgesehen davon, dass Sie einen hervorragenden Namen haben, können Sie folgende Anweisungen verwenden:
expect(asyncToResultingValue()).to.eventually.equal(true)
Kann bestätigen , funktioniert sehr gut für Mocha + Chai.
https://github.com/domenic/chai-as-promised
quelle
Sehr verwandt mit und inspiriert von Jean Vincents Antwort , verwenden wir eine Hilfsfunktion, die seiner Funktion ähnlich
check
ist, aber wir nennen sieeventually
stattdessen (dies hilft, sie mit den Namenskonventionen von Chai-as-versprochen in Einklang zu bringen). Es gibt eine Funktion zurück, die eine beliebige Anzahl von Argumenten akzeptiert und diese an den ursprünglichen Rückruf übergibt. Auf diese Weise können Sie einen zusätzlichen verschachtelten Funktionsblock in Ihren Tests entfernen und jede Art von asynchronem Rückruf verarbeiten. Hier steht es in ES2015:function eventually(done, fn) { return (...args) => { try { fn(...args); done(); } catch (err) { done(err); } }; };
Anwendungsbeispiel:
describe("my async test", function() { it("should fail", function(done) { setTimeout(eventually(done, (param1, param2) => { assert.equal(param1, "foo"); // this should pass assert.equal(param2, "bogus"); // this should fail }), 100, "foo", "bar"); }); });
quelle
Ich weiß, dass es viele Wiederholungsantworten und vorgeschlagene Pakete gibt, um dies zu lösen, aber ich habe nicht gesehen, dass die oben genannten einfachen Lösungen ein präzises Muster für die beiden Anwendungsfälle bieten. Ich poste dies als konsolidierte Antwort für andere, die Pasta kopieren möchten:
Ereignisrückrufe
function expectEventCallback(done, fn) { return function() { try { fn(...arguments); } catch(error) { return done(error); } done(); }; }
Rückrufe im Knotenstil
function expectNodeCallback(done, fn) { return function(err, ...args) { if (err) { return done(err); } try { fn(...args); } catch(error) { return done(error); } done(); }; }
Beispiel Verwendung
it('handles event callbacks', function(done) { something.on('event', expectEventCallback(done, (payload) => { expect(payload).to.have.propertry('foo'); })); }); it('handles node callbacks', function(done) { doSomething(expectNodeCallback(done, (payload) => { expect(payload).to.have.propertry('foo'); })); });
quelle
Basierend auf diesem Link von @richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/ kann description ein zurückgegebenes Versprechen verwenden, wenn Sie das Erledigte weglassen Parameter.
Der einzige Nachteil ist, dass es dort ein Versprechen geben muss, keine asynchrone Funktion (Sie können es mit einem Versprechen abschließen, du). In diesem Fall kann der Code jedoch extrem reduziert werden.
Dabei werden Fehler entweder in der anfänglichen Funktion funcThatReturnsAPromise oder in den Erwartungen berücksichtigt:
it('should test Promises', function () { // <= done removed return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added .then(response => expect(response).to.have.property('ok', 1)); });
quelle
Ich habe es gelöst,
try/catch
um eine Funktion zu extrahieren .function asyncExpect(test, done){ try{ test(); done(); } catch(error){ done(error); } }
Dann
it()
rufe ich an:it('shall update a host', function (done) { testee.insertHost({_id: 'host_id'}) .then(response => { asyncExpect(() => { expect(response).to.have.property('ok', 1); expect(response).to.have.property('nModified', 1); }, done); }); });
Es ist auch debuggbar.
quelle
Timer während Tests und Async klingen ziemlich rau. Es gibt eine Möglichkeit, dies mit einem vielversprechenden Ansatz zu erreichen.
const sendFormResp = async (obj) => { const result = await web.chat.postMessage({ text: 'Hello world!', }); return result }
Diese asynchrone Funktion verwendet einen Webclient (in diesem Fall Slacks SDK). Das SDK kümmert sich um die Asynchronität des API-Aufrufs und gibt eine Nutzlast zurück. Wir können dann die Nutzlast in Chai testen, indem wir
expect
gegen das im asynchronen Versprechen zurückgegebene Objekt laufen .describe("Slack Logic For Working Demo Environment", function (done) { it("Should return an object", () => { return sdkLogic.sendFormResp(testModels.workingModel).then(res => { expect(res).to.be.a("Object"); }) }) });
quelle
Was für mich bei icm Mocha / Chai sehr gut funktioniert hat, war der fakeTimer aus Sinons Bibliothek. Stellen Sie den Timer im Test bei Bedarf einfach vor.
var sinon = require('sinon'); clock = sinon.useFakeTimers(); // Do whatever. clock.tick( 30000 ); // Advances the JS clock 30 seconds.
Hat den zusätzlichen Vorteil, dass der Test schneller abgeschlossen wird.
quelle
Sie können auch das Domänenmodul verwenden. Zum Beispiel:
var domain = require('domain').create(); domain.run(function() { // place you code here }); domain.on('error',function(error){ // do something with error or simply print it });
quelle