Estoy ejecutando algunas pruebas asincrónicas en Mocha usando el Browser Runner y estoy tratando de usar las afirmaciones de estilo de espera de Chai:
window.expect = chai.expect;
describe('my test', function() {
it('should do something', function (done) {
setTimeout(function () {
expect(true).to.equal(false);
}, 100);
}
}
Esto no me da el mensaje normal de afirmación fallida, en su lugar obtengo:
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)
Entonces, obviamente, está detectando el error, simplemente no lo muestra correctamente. ¿Alguna idea de como hacer esto? Supongo que podría simplemente llamar "terminado" con un objeto de error, pero luego pierdo toda la elegancia de algo como Chai y se vuelve muy torpe ...
javascript
unit-testing
mocha
Thomas Parslow
fuente
fuente
chai-as-promised
complemento ...Respuestas:
Su prueba asincrónica genera una excepción, en
expect()
aciones fallidas , que no se puede capturarit()
porque la excepción se lanza fuera delit()
alcance de.La excepción capturada que ve que se muestra se captura usando
process.on('uncaughtException')
bajo nodo o usandowindow.onerror()
en el navegador.Para solucionar este problema, debe capturar la excepción dentro de la función asincrónica llamada por
setTimeout()
para llamardone()
con la excepción como primer parámetro. También necesita llamardone()
sin parámetro para indicar el éxito, de lo contrario, mocha informaría un error de tiempo de espera porque su función de prueba nunca habría señalado que se hizo: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 } }
Hacerlo en todos sus casos de prueba es molesto y no SECO, por lo que es posible que desee proporcionar una función para hacer esto por usted. Llamemos a esta función
check()
:function check( done, f ) { try { f(); done(); } catch( e ) { done( e ); } }
Con
check()
ahora puede reescribir sus pruebas asincrónicas de la siguiente manera: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 ); } }
fuente
Aquí están mis pruebas de aprobación para promesas de ES6 / ES2015 y async / await de ES7 / ES2016. Espero que esto proporcione una buena respuesta actualizada para cualquiera que esté investigando este tema:
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') })) })
fuente
Si le gusta lo prometido, pruebe Chai como Promised + Q , que permite algo como esto:
doSomethingAsync().should.eventually.equal("foo").notify(done);
fuente
Pregunté lo mismo en la lista de correo de Mocha. Básicamente me dijeron esto: escribir una prueba asincrónica con Mocha y Chai:
if (err) done(err);
done()
.Resolvió mi problema y no cambió ni una sola línea de mi código en el medio (expectativas de Chai, entre otras). No
setTimout
es la forma de realizar pruebas asíncronas.Aquí está el enlace a la discusión en la lista de correo .
fuente
setTimeout
función utilizada como ejemplo en esta pregunta no tiene ningún error en su devolución de llamada.Publiqué un paquete que resuelve este problema.
Primero instale el
check-chai
paquete:npm install --save check-chai
Luego, en sus pruebas, use
chai.use(checkChai);
y luego use lachai.check
función auxiliar como se muestra a continuación: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'); }); }); });
Por ¿Hay una manera de conseguir Chai trabajar con pruebas Mocha asíncronos? Publiqué esto como un paquete de NPM.
Consulte https://github.com/niftylettuce/check-chai para obtener más información.
fuente
¡Prueba chaiAsPromised! Además de tener un nombre excelente, puede usar declaraciones como:
expect(asyncToResultingValue()).to.eventually.equal(true)
Puedo confirmar , funciona muy bien para Mocha + Chai.
https://github.com/domenic/chai-as-promised
fuente
Muy relacionado e inspirado por la respuesta de Jean Vincent , empleamos una función auxiliar similar a su
check
función, pero la llamamos en sueventually
lugar (esto ayuda a que coincida con las convenciones de nomenclatura de chai-as-prometida). Devuelve una función que toma cualquier número de argumentos y los pasa a la devolución de llamada original. Esto ayuda a eliminar un bloque de funciones anidado adicional en sus pruebas y le permite manejar cualquier tipo de devolución de llamada asíncrona. Aquí está escrito en ES2015:function eventually(done, fn) { return (...args) => { try { fn(...args); done(); } catch (err) { done(err); } }; };
Ejemplo de uso:
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"); }); });
fuente
Sé que hay muchas respuestas repetidas y paquetes sugeridos para resolver esto, sin embargo, no he visto que las soluciones simples anteriores ofrezcan un patrón conciso para los dos casos de uso. Estoy publicando esto como una respuesta consolidada para otros que desean copiar pasta:
devoluciones de llamada de eventos
function expectEventCallback(done, fn) { return function() { try { fn(...arguments); } catch(error) { return done(error); } done(); }; }
devoluciones de llamada de estilo de nodo
function expectNodeCallback(done, fn) { return function(err, ...args) { if (err) { return done(err); } try { fn(...args); } catch(error) { return done(error); } done(); }; }
uso de ejemplo
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'); })); });
fuente
Basado en este enlace proporcionado por @richardforrester http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/ , describe puede usar una Promesa devuelta si omite el hecho parámetro.
El único inconveniente es que tiene que haber una Promesa allí, no una función asincrónica (puedes envolverla con una Promesa, tú). Pero en este caso, el código puede reducirse en extremo.
Tiene en cuenta las fallas de la función inicial funcThatReturnsAPromise o de las expectativas:
it('should test Promises', function () { // <= done removed return testee.funcThatReturnsAPromise({'name': 'value'}) // <= return added .then(response => expect(response).to.have.property('ok', 1)); });
fuente
Lo resolví extrayendo
try/catch
a una función.function asyncExpect(test, done){ try{ test(); done(); } catch(error){ done(error); } }
Entonces en
it()
llamo: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); }); });
También es debugable.
fuente
Los temporizadores durante las pruebas y async suenan bastante difíciles. Hay una manera de hacer esto con un enfoque basado en promesas.
const sendFormResp = async (obj) => { const result = await web.chat.postMessage({ text: 'Hello world!', }); return result }
Esta función asincrónica utiliza un cliente web (en este caso, es Slacks SDK). El SDK se encarga de la naturaleza asincrónica de la llamada a la API y devuelve una carga útil. Luego, podemos probar la carga útil dentro de chai ejecutando
expect
el objeto devuelto en la promesa asincrónica.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"); }) }) });
fuente
Lo que funcionó muy bien para mí icm Mocha / Chai fue el fakeTimer de la biblioteca de Sinon. Simplemente avance el temporizador en la prueba cuando sea necesario.
var sinon = require('sinon'); clock = sinon.useFakeTimers(); // Do whatever. clock.tick( 30000 ); // Advances the JS clock 30 seconds.
Tiene la ventaja adicional de que la prueba se completa más rápido.
fuente
También puede utilizar el módulo de dominio. Por ejemplo:
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 });
fuente