Mocha: La prueba asíncrona falla con el tiempo de espera en lugar del error de afirmación

Creado en 5 feb. 2014  ·  25Comentarios  ·  Fuente: mochajs/mocha

Esta prueba:

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        expect(returnValue).to.equal(42);
        done();
      });

    });

Esta prueba falla con timeout of 2000ms exceeded lugar de error de afirmación. Supongo que eso se debe a que la llamada expect() arroja un error y la llamada done() nunca se ejecuta, y me pregunto si hay una mejor manera de probar este tipo de código.

Comentario más útil

Me encontré con un problema similar y, finalmente, me di cuenta de que no debería usar done al probar funciones asincrónicas con promesas, en su lugar, solo devuelva la promesa. Entonces debería poder hacer esto sin el tiempo de espera, por ejemplo:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Todos 25 comentarios

¿Se resuelve alguna vez aPromise ? Si no es así, no hay más remedio que lanzar un tiempo muerto.

@NickHeiner Sí, se resuelve; y luego expect() encuentra que returnValue no es equal(42) y lanza.

@gurdiga, ¿cómo sabe que se lanza si obtiene un tiempo de espera y no un error de afirmación?

@hallas @NickHeiner Aquí está lo que se ejecuta: http://jsfiddle.net/gurdiga/p9vmj/.

@gurdiga me parece que tu promesa tiene su propio error de captura. Intente agregar .catch(done) a su promesa y creo que funcionará según lo previsto.

@hallas Wow: ¡_ esa_ fue la respuesta! :) aPromise.finally() parece ser el ajuste perfecto para poner done en: también necesita ser llamado cuando se resuelve la promesa. ;)

¡Gracias!

Me siento estupido.

Creo que finalmente lo entendí: cuando algo arroja cualquiera de las funciones del controlador de la promesa, ya sea la que se pasó a .then() , a .catch() oa .finally() , el error es manejado por la biblioteca de promesas. De esta manera, el ejecutor de la prueba nunca ve el error real, nunca se llama al done() (porque algo antes arrojó un error de aserción), por lo que el error de tiempo de espera es todo lo que obtiene del ejecutor de la prueba.

Para salir de la promesa, uso setTimeout() :

    it('returns the correct value', function(done) {
      var returnValue = 5;

      aPromise.then(function() {
        setTimeout(function() {
          expect(returnValue).to.equal(42);
          done();
        });
      });
    });

Descubrí que esta es la única forma de obtener los mensajes de error adecuados y el comportamiento del corredor de pruebas.

Con done pasado a .catch() o .finally() la prueba se considera aprobada en cualquier caso, por lo que si hay errores de afirmación, nunca los verá.

Me encontré con un problema similar y, finalmente, me di cuenta de que no debería usar done al probar funciones asincrónicas con promesas, en su lugar, solo devuelva la promesa. Entonces debería poder hacer esto sin el tiempo de espera, por ejemplo:

it('returns the correct value', function() {
    var returnValue = 5;

    return aPromise.then(function() {
        expect(returnValue).to.equal(42);
    });
});

Creo que este problema aún existe. Recibo un problema de tiempo de espera que no se puede resolver devolviendo una promesa porque mi módulo no usa promesas.

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();
    });
});
  • Envolver la instancia en Promise parece excesivo.
  • Envolver cada afirmación en un try / catch también parece excesivo y, lo que es más importante, da como resultado Error: done() called multiple times .

Ideas:
http://staxmanade.com/2015/11/testing-asyncronous-code-with-mochajs-and-es7-async-await/

Con respecto a las recomendaciones de la publicación del blog, no sé sobre el manejo de errores de la función asíncrona, pero para las promesas simples, la recomendación try-catch es extraña: el intento de promesa original sin try-catch era casi correcto, solo necesitaba usar .catch(done) lugar de usar el segundo parámetro para then como done . (Por supuesto, dado que Mocha tiene soporte directo para las promesas, también puede simplemente devolver la promesa, pero por lo que vale ...) El problema en el ejemplo de promesa inicial no fue la ausencia de try-catch, fue que el segundo controlador a then no se llama con las excepciones lanzadas por el primer controlador, mientras que un catch es; No sé cuál fue la razón fundamental para que las promesas se diseñen de esa manera, pero así son las promesas. Además, si hubiera habido varios then s por alguna razón, solo se necesitaría un único .catch(done) final, que es un punto de superioridad sobre try-catch dentro de los manejadores (además del hecho de que .catch(done) es menos repetitivo para empezar).

En cuanto a tu API:

  1. ¿Estás seguro de que se están convocando ambos eventos y en el orden correcto? Si no fuera así, ¿cómo convertiría su prueba en una falla normal?
  2. ¿Qué sucede normalmente con las excepciones que se lanzan desde sus controladores de eventos? Si no se propagan de la forma en que lo hacen en código síncrono y, en cambio, están destinados a ser escuchados en la API (por ejemplo, con .on("error", function(error) {...}) ), nunca llegarán a Mocha a menos que los escuche y tenga el oyente llama a done con el error (o simplemente usa done con el oyente si al oyente se le pasa el error como su primer parámetro, por ejemplo, .on("error", done) . debe escribirse una vez por prueba, en lugar de una vez por controlador de eventos, como .catch(done) en una promesa.
  1. Sí, y utilizo un evento "end" / "drain" para verificar si se establecieron booleanos en los otros eventos.
  2. Sucede el tiempo de espera. Estoy tratando de encontrar una alternativa limpia y sencilla.

Lo siento, todavía no sé cómo se supone que su API informa errores.

Sí, hasta ahora solo he estado confiando en los tiempos de espera para _cuando_ algo falla, luego indagando manualmente para averiguar _cómo / por qué_. Afortunadamente, las cosas rara vez se rompen, pero eso no es una excusa para la falta de un mejor diseño (por mi parte, en su mayoría).

@stevenvachon : Perdóname de antemano, pero no veo un problema inmediato con tu ejemplo. Mocha debe manejar las aserciones hechas en sus oyentes de eventos a través del mapeo uncaughtException (a menos que la implementación del emisor de eventos esté detectando errores de oyentes y emitiendo un evento error o algo así, que luego sigue siendo fácil de resolver).

Ahora bien, si su implementación bajo el capó está usando Promesas, pero emite eventos en lugar de exponer la Promesa, sus afirmaciones de hecho serán "devoradas". La forma en que soluciono este problema es usar unhandledRejection .

Normalmente pongo esto en un script de configuración que se ejecuta antes de mis pruebas:

process.on('unhandledRejection', function (reason)
{
    throw reason;
});

Nota: esto puede requerir un poco más de esfuerzo para funcionar en los navegadores.

Espero ver que Mocha soporte esto como lo hace uncaughtException ya que este es un caso de uso común; ¡Solo porque use Promises no significa que quiera devolvérselos a la persona que llama!

Tener el mismo problema con [email protected]

    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.

El .catch es incorrecto. error => resolve es equivalente a function(error) { return resolve } , lo que significa que resolve no se llamará y el error se ignorará. Lo que quieres es llamar a resolve con el error, que sería error => resolve(error) . Por supuesto, pasar una función de devolución de llamada X que simplemente llama a la función Y con los mismos argumentos con los que se llama X es equivalente a pasar Y como devolución de llamada, por lo que incluso .catch(error => resolve(error)) podría simplificarse a .catch(resolve) . (Solo necesitaría no pasar resolve directamente si lo estuviera pasando a then y, por lo tanto, necesita evitar pasar el parámetro de resultado de then a resolve para evitar que se trate como un error: then(()=>resolve()) lugar de solo .then(resolve) ; pero como está utilizando la devolución de llamada then para las afirmaciones, esto no aparece .)

(Además, idiomáticamente, resolve aquí probablemente debería tener un nombre similar a done , ya que maneja tanto el éxito como el fracaso y juzga cuál es cuál en función de si se llamó con un argumento o no. De ahí el nombre en el mensaje de error de Mocha. Pero eso puede ser un punto discutible; sigue leyendo.)

Sin embargo, en este caso, puede simplificarlo aún más simplemente devolviendo la promesa y sin usar el parámetro test done en absoluto, ya que Mocha esperará a que la promesa tenga éxito o falle como indicando el éxito o el fracaso de la prueba (siempre que no haya un parámetro done para la función de prueba; el comportamiento en el caso de que se usen ambos todavía se está eliminando):

it('Convert files into base64', () => {
    let files = Promise.all(promises);
    return files
        .then(([actual, expected]) => {
            assert.equal(actual, expected, 'Files not are equal');
        })
});

@lsphillips que funciona para mí. ¡¡Gracias!! Espero que mocha también lo admita de forma predeterminada. Acabo de crear el # 2640.

¡Me tomó un tiempo resolver esto! Según las respuestas anteriores , estas son las dos opciones:

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 ¡ Gracias por la idea de setTimeout ()! Tuve un problema similar, ¡pero ahora puedo obtener al menos los mensajes de error adecuados!

En mi escenario, usé Nightmare para end2end tests. La solución para mí fue usar .catch(done) . Puede llamar a done(error) dentro de otra devolución de llamada de captura como en el ejemplo siguiente.

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 eso funciona, pero puedes hacer esto en su lugar:

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!')
        })
    })
  })

No es necesario que llames a done() si devuelves una promesa. Vea la publicación de mi blog Uso de Async / Await con Mocha, Express y Mongoose

Usé el siguiente script pero obtuve el mismo error de exceder el tiempo de espera.

Myscript:

describe ("getBillingDetail", función asíncrona () {
this.timeout (55000);
it.only ("comprobar el nombre de trabajo válido dado", función asíncrona (hecho) {
this.timeout (55000);
var result = await url.getBillingDetail ('12254785565647858');
console.log (resultado);
asert.equal (resultado, verdadero);
});
});

Error: se superó el tiempo de espera de 55000 ms. Para pruebas asíncronas y ganchos, asegúrese de que se llame a "done ()"; si devuelve una Promesa, asegúrese de que se resuelva.

Deje de escribir lo mismo en varios números cerrados. No pase una devolución de llamada realizada a funciones asíncronas. Leer la documentación sobre pruebas asíncronas

@Munter eliminé la devolución de llamada realizada, pero este error se repite

Parece que tu promesa nunca se volvió a cumplir.

¿Fue útil esta página
0 / 5 - 0 calificaciones