Mocha: O teste assíncrono falha com tempo limite em vez de erro de declaração

Criado em 5 fev. 2014  ·  25Comentários  ·  Fonte: mochajs/mocha

Esse teste:

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

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

    });

Este teste falha com timeout of 2000ms exceeded vez de erro de declaração. Acho que é porque a chamada expect() gera um erro e o done() nunca é executado, e estou me perguntando se há uma maneira melhor de testar esse tipo de código.

Comentários muito úteis

Eu encontrei um problema semelhante e finalmente percebi que você não deveria usar done ao testar funções assíncronas com promessas, em vez disso, apenas retorne a promessa. Portanto, você deve ser capaz de fazer isso sem o tempo limite, por exemplo:

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

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

Todos 25 comentários

aPromise resolve alguma vez? Do contrário, não há muita escolha a não ser lançar um tempo limite.

@NickHeiner Sim, resolve; e então expect() descobre que returnValue não é equal(42) e arremessa.

@gurdiga como saber se você obtém um tempo limite e não um erro de asserção?

@hallas @NickHeiner Aqui está o que está em execução: http://jsfiddle.net/gurdiga/p9vmj/.

@gurdiga parece-me que sua promessa tem seu próprio erro de captura. Tente adicionar .catch(done) à sua promessa e acho que funcionará conforme o esperado.

@hallas Wow: _that_ foi a resposta! :) aPromise.finally() parece ser o ajuste perfeito para colocar done : ele também precisa ser chamado quando a promessa for resolvida. ;)

Obrigado!

Eu me sinto estúpido.

Acho que finalmente entendi: quando algo joga em qualquer uma das funções do manipulador da promessa, seja aquela passada para .then() , para .catch() ou para .finally() , o erro é manipulado pela biblioteca de promessa. Dessa forma, o executor de teste nunca vê o erro real, o done() nunca é chamado (porque algo antes dele gerou um erro de asserção) e, portanto, o erro de tempo limite é tudo o que você obtém do executor de teste.

Para sair da promessa, eu uso setTimeout() :

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

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

Descobri que esta é a única maneira de obter mensagens de erro adequadas e testar o comportamento do executor.

Com done passado para .catch() ou .finally() o teste é considerado aprovado em qualquer caso, portanto, se houver erros de asserção, você nunca os verá.

Eu encontrei um problema semelhante e finalmente percebi que você não deveria usar done ao testar funções assíncronas com promessas, em vez disso, apenas retorne a promessa. Portanto, você deve ser capaz de fazer isso sem o tempo limite, por exemplo:

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

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

Acho que esse problema ainda existe. Estou recebendo um problema de tempo limite que não pode ser resolvido retornando uma promessa porque meu módulo não usa promessas.

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();
    });
});
  • Encapsular a instância em Promise parece excessivo.
  • Envolver cada afirmação em try / catch também parece excessivo e, mais importante, resulta em Error: done() called multiple times .

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

No que diz respeito às recomendações da postagem do blog, não sei sobre o tratamento de erros de função assíncrona, mas para promessas simples, a recomendação try-catch é estranha: a tentativa de promessa original sem try-catch estava quase correta, bastava usar .catch(done) vez de usar o segundo parâmetro para then como done . (Concedido, como o Mocha tem suporte direto para promessas, você também pode simplesmente devolver a promessa, mas valha a pena ...) O problema no exemplo de promessa inicial não era a ausência de try-catch, era que o segundo manipulador to then não é chamado com exceções lançadas pelo primeiro manipulador, enquanto um catch é; Não sei qual era o motivo para as promessas serem elaboradas dessa forma, mas é assim que as promessas são. Além disso, se houvesse vários then s por algum motivo, apenas um único .catch(done) final seria necessário, o que é um ponto de superioridade sobre o try-catch dentro dos manipuladores (no topo do fato de que .catch(done) é menos clichê para começar).

Quanto à sua API:

  1. Tem certeza de que os dois eventos estão sendo chamados e na ordem certa? Se não fossem, como seu teste transformaria isso em uma falha normal?
  2. O que normalmente acontece com as exceções lançadas de seus manipuladores de eventos? Se eles não se propagam da maneira que fazem no código síncrono e, em vez disso, devem ser ouvidos na API (por exemplo, com .on("error", function(error) {...}) ), então eles nunca chegarão ao Mocha a menos que você os escute e tenha o ouvinte chamar done com o erro (ou apenas usar done com o ouvinte se o ouvinte receber o erro como seu primeiro parâmetro, por exemplo, .on("error", done) . Presumivelmente, isso também apenas precisa ser escrito uma vez por teste, em vez de uma vez por manipulador de eventos, como .catch(done) em uma promessa.
  1. Sim, e eu uso um evento "end" / "drain" para verificar se os booleanos nos outros eventos foram definidos.
  2. O tempo limite acontece. Estou tentando encontrar uma alternativa enxuta e limpa.

Ainda não sei como sua API deve relatar erros.

Sim, até agora tenho confiado nos tempos limites para _quando_ algo falha e, em seguida, cavando manualmente para descobrir _como / por quê_. Felizmente, raramente as coisas quebram, mas isso não é desculpa para a falta de um design melhor (da minha parte, principalmente).

@stevenvachon : Perdoe-me antecipadamente, mas não vejo um problema imediato com seu exemplo. As afirmações feitas em seus ouvintes de evento devem ser tratadas pelo Mocha por meio do mapeamento uncaughtException (a menos que a implementação do emissor de evento esteja capturando erros de ouvinte e emitindo um evento error ou algo assim, que ainda é fácil de resolver).

Agora, se sua implementação nos bastidores está usando Promises, mas emite eventos em vez de expor a Promessa, suas afirmações serão realmente "comidas". A maneira de contornar esse problema é usar unhandledRejection .

Normalmente coloco isso em um script de configuração que é executado antes dos meus testes:

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

Nota: Isso pode precisar de um pouco de graxa de cotovelo extra para funcionar nos navegadores.

Espero ver o Mocha suportar isso como uncaughtException pois este é um caso de uso comum; só porque uso Promises não significa que quero devolvê-las ao chamador!

Tendo o mesmo problema com [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.

O .catch está errado. error => resolve é equivalente a function(error) { return resolve } , o que significa que resolve não será chamado e o erro será ignorado. O que você quer é chamar resolve com o erro, que seria error => resolve(error) . Claro, passar uma função de retorno de chamada X que simplesmente chama a função Y com os mesmos argumentos com os quais X é chamada é equivalente a apenas passar Y como o callback, então mesmo .catch(error => resolve(error)) poderia ser simplificado para .catch(resolve) . (Você só precisaria não passar resolve diretamente se o estivesse passando para then e, portanto, precisava evitar passar o parâmetro de resultado de then para resolve para evitar que seja tratado como um erro: then(()=>resolve()) vez de apenas .then(resolve) ; mas como você está usando o retorno de chamada then para as afirmações, isso não aparece .)

(Além disso, linguisticamente, resolve aqui provavelmente deve ser nomeado algo na linha de done , uma vez que lida com o sucesso e o fracasso e julga que é baseado em se foi chamado com um argumento ou não. Daí o nome na mensagem de erro de Mocha. Mas isso pode ser um ponto discutível; continue a ler.)

No entanto, neste caso, você pode simplificar ainda mais apenas retornando a promessa e não usando o parâmetro de teste concluído, já que o Mocha irá esperar que a promessa seja bem-sucedida ou falhe, indicando sucesso ou falha do teste (desde que não haja nenhum parâmetro feito para a função de teste; o comportamento no caso de ambos serem usados ​​ainda está sendo hash):

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 mim. Obrigado!! Espero ver o mocha suportar isso por padrão também. Acabei de criar # 2640.

Levei um tempo para resolver isso! Com base nas respostas acima , estas são as duas opções:

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 Obrigado pela ideia setTimeout ()! Tive um problema semelhante, mas agora posso obter pelo menos as mensagens de erro adequadas!

No meu cenário, usei o Nightmare para finalizar os testes. A solução para mim foi usar .catch(done) . Você pode chamar done(error) dentro de outro retorno de chamada catch como o exemplo abaixo.

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 que funciona, mas você pode fazer isso:

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

Você não precisa ligar para done() se retornar uma promessa. Veja minha postagem no blog Usando Async / Await com Mocha, Express e Mongoose

Usei o script abaixo, mas obtive o mesmo erro de ultrapassagem de tempo limite.

Myscript:

describe ("getBillingDetail", função assíncrona () {
this.timeout (55000);
it.only ("verificar nome de trabalho válido fornecido", função assíncrona (concluído) {
this.timeout (55000);
var result = await url.getBillingDetail ('12254785565647858');
console.log (resultado);
assert.equal (resultado, verdadeiro);
});
});

Erro: Tempo limite de 55.000 ms excedido. Para testes assíncronos e ganchos, certifique-se de que "done ()" seja chamado; se retornar uma promessa, certifique-se de que resolve.

Pare de soletrar a mesma coisa em vários fascículos encerrados. Não passe um retorno de chamada concluído para funções assíncronas. Leia a documentação sobre testes assíncronos

@Munter eu removi o retorno de chamada concluído, mas esses erros ocorrem novamente

Parece que sua promessa nunca foi resolvida.

Esta página foi útil?
0 / 5 - 0 avaliações