Przeprowadzam kilka testów asynchronicznych w Mocha przy użyciu Browser Runner i próbuję użyć asercji oczekiwanego stylu Chai:
window.expect = chai.expect;
describe('my test', function() {
it('should do something', function (done) {
setTimeout(function () {
expect(true).to.equal(false);
}, 100);
}
}
To nie daje mi normalnego komunikatu o niepowodzeniu potwierdzenia, zamiast tego otrzymuję:
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)
Więc oczywiście wychwytuje błąd, po prostu nie wyświetla go poprawnie. Jakieś pomysły, jak to zrobić? Myślę, że mógłbym po prostu nazwać „gotowe” z obiektem błędu, ale potem tracę całą elegancję czegoś takiego jak Chai i staje się bardzo niezgrabne ...
javascript
unit-testing
mocha
Thomas Parslow
źródło
źródło
chai-as-promised
wtyczkę ...Odpowiedzi:
Twój test asynchroniczny generuje wyjątek w przypadku niepowodzenia
expect()
, którego nie można przechwycić,it()
ponieważ wyjątek jest zgłaszany pozait()
zakresem.Przechwycony wyjątek, który widzisz jako wyświetlany, jest przechwytywany za pomocą
process.on('uncaughtException')
węzła lub za pomocąwindow.onerror()
w przeglądarce.Aby rozwiązać ten problem, należy przechwycić wyjątek w ramach funkcji asynchronicznej wywoływanej przez
setTimeout()
, aby wywołaćdone()
wyjątek jako pierwszy parametr. Musisz również wywołaćdone()
bez parametru, aby wskazać sukces, w przeciwnym razie mokka zgłosi błąd przekroczenia limitu czasu, ponieważ Twoja funkcja testowa nigdy nie zasygnalizowałaby, że została wykonana: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 } }
Robienie tego we wszystkich przypadkach testowych jest denerwujące i nie jest SUCHE, więc możesz chcieć udostępnić funkcję, która zrobi to za Ciebie. Nazwijmy tę funkcję
check()
:function check( done, f ) { try { f(); done(); } catch( e ) { done( e ); } }
Dzięki
check()
możesz teraz przepisać testy asynchroniczne w następujący sposób: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 ); } }
źródło
Oto moje zaliczone testy obietnic ES6 / ES2015 i async / await ES7 / ES2016. Mam nadzieję, że zapewnia to miłą zaktualizowaną odpowiedź dla każdego, kto bada ten temat:
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') })) })
źródło
Jeśli podoba Ci się obietnica, wypróbuj Chai jako obiecaną + Q , które pozwalają na coś takiego:
doSomethingAsync().should.eventually.equal("foo").notify(done);
źródło
O to samo zapytałem na liście mailingowej Mocha. Zasadniczo powiedzieli mi: napisać test asynchroniczny z Mocha i Chai:
if (err) done(err);
done()
.To rozwiązało mój problem i nie zmieniło ani jednej linii kodu pomiędzy (między innymi oczekiwania Chai). Plik
setTimout
jest to sposób na wykonywanie testów asynchronicznych.Oto link do dyskusji na liście mailingowej .
źródło
setTimeout
Funkcja używany jako przykład w tej kwestii nie ma żadnego błędu w jego zwrotnego.Opublikowałem pakiet, który rozwiązuje ten problem.
Najpierw zainstaluj
check-chai
pakiet:npm install --save check-chai
Następnie w swoich testach użyj,
chai.use(checkChai);
a następnie użyjchai.check
funkcji pomocniczej, jak pokazano poniżej: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 Czy istnieje sposób, aby Chai działał z asynchronicznymi testami Mocha?Opublikowałem to jako pakiet NPM.
Więcej informacji można znaleźć pod adresem https://github.com/niftylettuce/check-chai .
źródło
Wypróbuj chaiAsPromised! Oprócz doskonałej nazwy możesz używać takich instrukcji:
expect(asyncToResultingValue()).to.eventually.equal(true)
Potwierdzam , działa bardzo dobrze dla Mocha + Chai.
https://github.com/domenic/chai-as-promised
źródło
Bardzo mocno powiązana i zainspirowana odpowiedzią Jeana Vincenta , stosujemy funkcję pomocniczą podobną do jego
check
funkcji, ale nazywamy jąeventually
zamiast tego (pomaga to dopasować się do konwencji nazewnictwa czaj-zgodnie z obietnicą). Zwraca funkcję, która przyjmuje dowolną liczbę argumentów i przekazuje je do pierwotnego wywołania zwrotnego. Pomaga to wyeliminować dodatkowy zagnieżdżony blok funkcyjny w testach i umożliwia obsługę dowolnego typu wywołania zwrotnego asynchronicznego. Tutaj jest napisane w ES2015:function eventually(done, fn) { return (...args) => { try { fn(...args); done(); } catch (err) { done(err); } }; };
Przykładowe zastosowanie:
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"); }); });
źródło
Wiem, że istnieje wiele powtarzających się odpowiedzi i sugerowanych pakietów rozwiązania tego problemu, ale nie widziałem, aby proste rozwiązania powyżej oferowały zwięzły wzór dla dwóch przypadków użycia. Przesyłam to jako skonsolidowaną odpowiedź dla innych, którzy chcą skopiować makaron:
wywołania zwrotne zdarzeń
function expectEventCallback(done, fn) { return function() { try { fn(...arguments); } catch(error) { return done(error); } done(); }; }
wywołania zwrotne w stylu węzła
function expectNodeCallback(done, fn) { return function(err, ...args) { if (err) { return done(err); } try { fn(...args); } catch(error) { return done(error); } done(); }; }
przykład użycia
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'); })); });
źródło
Na podstawie tego linku dostarczonego przez @richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/ , opisz, że możesz użyć zwróconej Obietnicy, jeśli pominiesz gotowe parametr.
Jedynym minusem musi być obietnica, a nie funkcja asynchroniczna (możesz ją owinąć obietnicą). Ale w tym przypadku kod można bardzo zmniejszyć.
Uwzględnia wady zarówno w funkcji początkowej funkcji, która zwraca promise, jak i oczekiwania:
it('should test Promises', function () { // <= done removed return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added .then(response => expect(response).to.have.property('ok', 1)); });
źródło
Rozwiązałem to wyodrębnianie
try/catch
do funkcji.function asyncExpect(test, done){ try{ test(); done(); } catch(error){ done(error); } }
Wtedy
it()
wzywam: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); }); });
Można go również debugować.
źródło
Liczniki czasu podczas testów i asynchronizacji brzmią dość szorstko. Jest na to sposób, stosując podejście oparte na obietnicach.
const sendFormResp = async (obj) => { const result = await web.chat.postMessage({ text: 'Hello world!', }); return result }
Ta funkcja asynchroniczna korzysta z klienta sieci Web (w tym przypadku jest to Slacks SDK). Zestaw SDK dba o asynchroniczny charakter wywołania interfejsu API i zwraca ładunek. Następnie możemy przetestować ładunek w chai, uruchamiając
expect
obiekt zwrócony w obietnicy asynchronicznej.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"); }) }) });
źródło
To, co działało bardzo dobrze dla mnie icm Mocha / Chai, to fałszywy Timer z Sinon's Library. W razie potrzeby wystarczy przesunąć licznik czasu w teście.
var sinon = require('sinon'); clock = sinon.useFakeTimers(); // Do whatever. clock.tick( 30000 ); // Advances the JS clock 30 seconds.
Ma dodatkową zaletę w postaci szybszego zakończenia testu.
źródło
Możesz również użyć modułu domeny. Na przykład:
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 });
źródło