unitarios unitarias test spec pruebas node javascript unit-testing mocha

javascript - unitarias - ¿Hay alguna forma de que Chai trabaje con pruebas de Mocha asíncronas?



pruebas unitarias node js (11)

Aquí están mis exámenes de aprobación para ES6 / ES2015 y ES7 / ES2016 async / await. Espero que esto proporcione una buena respuesta actualizada para cualquiera que investigue 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'') })) })

Estoy ejecutando algunas pruebas asincrónicas en Mocha usando Browser Runner y estoy tratando de usar las aserciones de estilo esperadas 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 de afirmación fallido normal, 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)

Por lo tanto, obviamente está captando el error, simplemente no se muestra correctamente. ¿Alguna idea de como hacer esto? Supongo que podría llamar "hecho" con un objeto de error, pero luego pierdo toda la elegancia de algo como Chai y se vuelve muy torpe ...


Basado en este enlace provisto por @richardforrester staxmanade.com/2015/11/… , describe puede usar una Promesa devuelta si omites el hecho parámetro.

El único inconveniente es que debe haber una promesa allí, no una función asíncrona (puedes envolverla con una promesa, tú). Pero en este caso, el código puede ser extremadamente reducido.

Tiene en cuenta los fallos 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)); });


Le pregunté lo mismo en la lista de correo de Mocha. Básicamente me dijeron esto: escribir una prueba asincrónica con Mocha y Chai:

  • siempre comience la prueba con if (err) done(err);
  • siempre termine la prueba con done() .

Resolvió mi problema y no modificó una sola línea de mi código en el medio (las expectativas de Chai entre otras). setTimout no es la forma de hacer pruebas asincrónicas.

Aquí está el enlace a la discusión en la lista de correo .


Lo que funcionó muy bien para mí icm Mocha / Chai fue el temporizador falso de Sinon''s Library. 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 tener la prueba completa más rápido.


Lo resolví extrayendo try/catch para una función.

function asyncExpect(test, done){ try{ test(); done(); } catch(error){ done(error); } }

Luego 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.


Muy relacionado e inspirado por la respuesta de Jean Vincent , empleamos una función auxiliar similar a su función de check , pero en eventually lugar la llamamos eventually (esto ayuda a que coincida con las convenciones de nomenclatura de chai-as-prometido). Devuelve una función que toma cualquier cantidad de argumentos y los pasa a la devolución de llamada original. Esto ayuda a eliminar un bloque de funciones adicional anidado 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"); }); });



Publiqué un paquete que resuelve este problema.

Primero instale el paquete check-chai :

npm install --save check-chai

Luego, en sus pruebas, use chai.use(checkChai); y luego use la función auxiliar chai.check 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''); }); }); });

¿Hay alguna manera de hacer que Chai trabaje con pruebas de Mocha asíncronas? Publiqué esto como un paquete de NPM.

Consulte github.com/niftylettuce/check-chai para obtener más información.


Si te gusta lo prometido, prueba con Chai como Promised + Q , que permite algo como esto:

doSomethingAsync().should.eventually.equal("foo").notify(done);


Su prueba asincrónica genera una excepción, en las expect() fallidas expect() , que no pueden ser capturadas por it() porque la excepción se emite fuera del alcance it() .

La excepción capturada que ve se muestra utilizando process.on(''uncaughtException'') debajo del nodo o usando window.onerror() en el navegador.

Para solucionar este problema, debe capturar la excepción dentro de la función asincrónica llamada por setTimeout() para llamar a done() con la excepción como primer parámetro. También debe llamar a done() sin ningún 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 indicado que se había realizado:

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


También puedes usar 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 });