Mocha: Erro: o método de resolução está especificado demais.

Criado em 2 ago. 2016  ·  84Comentários  ·  Fonte: mochajs/mocha

Este:

before(done => {
    return Promise
        .all([])
        .then(() => Model.insert(...)) // Bookshelf model returning a Bluebird Promise
        .then(() => done())
        .catch(done)
})

resultará em um erro Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.

Docs dizem:

No Mocha v3.0.0 e mais recente, retornar uma Promise e chamar done () resultará em uma exceção, pois isso geralmente é um erro:

A chamada de modelo está sendo resolvida com Promise.<Object> da entrada recém-inserida, entretanto, se eu omitir .then(() => done()) , o tempo limite do teste será atingido.

confirmed-bug

Comentários muito úteis

async functions (babel) com done também quebram.

Todos 84 comentários

async functions (babel) com done também quebram.

Como afirma o erro, você não deve fornecer uma função com aridade> 0 (o que significa que está aceitando um retorno de chamada done ), _e_ retornar uma promessa.

A maneira mais fácil de consertar seria omitir o return , mas como você está usando promessas, sugiro se livrar do retorno de chamada done , pois isso resultará em uma construção muito mais simples :

before(() => Promise.all([]).then(() => Model.insert(...)));

Aqui está um exemplo de async (essencialmente uma promessa) e done que quebra:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

@elado Esse é um caso de uso interessante, embora do ponto de vista do mocha seja a mesma situação - uma função que recebe um retorno de chamada done e retorna uma promessa. Eu o reescreveria para ser totalmente baseado em promessas:

it('fires change event when calling blah.doSomethingThatFiresChange', async function () {
  const blah = await getBlah()
  return new Promise(resolve => {
    blah.on('change', resolve)
    blah.doSomethingThatFiresChange()
  })
})

... mas eu acho que você está querendo dizer que, neste exemplo, seria melhor se mocha esperasse por _both_ a promessa ser resolvida e o retorno de chamada ser chamado?

Infelizmente, essa combinação em particular costuma ser um bug no teste, e é por isso que essa mensagem de erro foi adicionada em primeiro lugar.

... se eu omitir .then (() => done ()), o tempo limite do teste.

Isso parece um bug, em qualquer caso.

@ScottFreeCode Hmm, sim, parece ser porque o erro "overspecified" é emitido na função fornecida como o retorno de chamada done : https://github.com/mochajs/mocha/blob/4944e31ff60105815f4b314996a9861e73f6bfd2/lib/ runnable.js # L357 -L373

... mas podemos, é claro, determinar que temos que falhar com esse erro assim que a função retornar.

@ScottFreeCode Qual é a correção aqui?

Eu vou adivinhar que é "esperar que a promessa seja resolvida ou rejeitada, então rejeite com o erro 'superespecificado'"?

Se considerarmos done plus promessa no mesmo teste um erro, não vejo razão para não detectar o erro assim que uma função de teste que levou done retornar um promessa, como @papandreou sugeriu. Não faz muito sentido para mim tentar descobrir quais outros pontos devem acionar o erro, a menos que pretendamos permitir promessas e done juntos em alguns casos.

@ScottFreeCode Estou de acordo. Então é

  1. Detectar problema; instancie Error mas não chame done() com ele
  2. Espere até o cumprimento da promessa
  3. Rejeite com Error

Pergunta bônus: O que fazer com o resultado do cumprimento da promessa?

Ah, acho que entendi - mesmo quando detectamos o erro, precisamos deixar o teste rodar para que ele não execute seu código durante o próximo teste e, talvez, para que possamos também relatar o resultado do teste.

Um teste também poderia terminar chamando done sem resolver ou rejeitar a promessa? Nesse caso, esse é outro caso final que teremos de lidar.

Minha inclinação, re. o que fazer com o resultado do teste, é que se ele atingir o tempo limite (sem ter _nenhum_ chamado done ou resolvido / rejeitado a promessa), devemos apenas relatar o uso de done com a promessa (porque a confusão sobre a combinação pode muito bem ser o motivo pelo qual nunca chegou a nenhum deles e atingiu o tempo limite em vez disso), mas se conseguir terminar por qualquer um dos meios, então presumivelmente o resultado é válido (ou pelo menos de alguma forma significativo) e devemos relatar tanto o uso possivelmente errôneo de done e promessa juntos quanto o resultado do teste na esperança de que seja pelo menos um pouco útil.

É o melhor que posso pensar no momento, de qualquer maneira, mas posso ter mais insights se encontrar tempo para investigar esse problema.

Bem, podemos fazer isso em fases. A primeira seria garantir que se uma promessa fosse retornada e um retorno de chamada done fosse dado, então o Mocha quebraria de maneira amigável.

É _concebível_ que alguém poderia fazer algo assim (com Bluebird):

// to make this work, you need to omit `return`
it('should do something async for sure', function(done) {
  return somethingAsync()
    .then(makeAssertion)
    .asCallback(done);
});

Mas isso é apenas algo que você _pode_ fazer; Ainda estou para determinar um caso de uso para isso.

Acho que me confundi. Não tenho certeza se há um bug aqui.

Este é mais um problema do tipo "UI pobre"

O tempo limite de espera de done para ser chamado, embora uma promessa devolvida seja resolvida / rejeitada é definitivamente um bug, independentemente de querermos proibir que tais testes usem done e promessas juntas no primeiro Lugar, colocar. Isso deve usar o resultado da promessa e / ou erro porque a promessa e done foram ambos utilizados no mesmo teste, mas não apenas expirou porque um dos dois nunca foi concluído quando o outro o fez (o que é o que faz atualmente):

it("should not time out", function(done) {
  return new Promise(function(resolve, reject) {
    setTimeout(resolve.bind(42), 50)
  })
})

1) não deve expirar:
Erro: tempo limite de 2.000 ms excedido. Certifique-se de que o retorno de chamada done () esteja sendo chamado neste teste.

Vou olhar para o PR em qualquer caso ...

As partes interessadas devem assistir # 2413.

@briansipple

Recebo o mesmo erro usando vários ganchos assíncronos beforeEach na versão 3:

  beforeEach((cb) => connection.db.collection('users').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').remove({}, cb));
  beforeEach((cb) => connection.db.collection('accounts').insertOne(account1, cb));

Não é possível ainda usar ganchos assíncronos dessa forma?

Eu descobri meu problema: meus métodos dentro de cada gancho estão retornando uma promise (eu não sabia disso porque não estava usando promessas nesses casos) e obviamente também estou passando a função cb .

Não acho que seja uma boa ideia. Se alguém solicitar um retorno de chamada, acho que ele deixou suas intenções claras. O erro aqui é apenas um aborrecimento. Remova-o.

Não é realmente uma questão de intenção. Da descrição de # 1320:

Quando uma função de retorno de chamada é especificada e um objeto Promise é
retornado, a condição de resolução do Runnable é ambígua.

Não é ambíguo, o retorno de chamada foi solicitado. Como isso é ambíguo?

Eu concordo com que @RobertWHurst não há ambigüidade aqui.

Além disso, acho que essa questão pode ser algo "baseado em opinião" e os desenvolvedores terão diferentes pontos de vista. Considero que é muito extremista fazer uma mudança radical e forçar as pessoas a usarem dessa forma.

Não _forçamos_ ninguém a fazer nada. Lançamos um major, que por definição terá mudanças significativas. Nós não quebramos semver.

Eu não acho que ele quis dizer que você quebrou semver, acho que ele quis dizer que você quebrou Mocha. Essa mudança não torna as coisas mais fáceis para os desenvolvedores, ela reforça uma opinião.

beforeEach((cb) => connection.db.collection('users').remove({}, cb));
            ^^

^^ Isso não é ambíguo. Está bem claro o que o autor espera. O autor saiu de seu caminho para solicitar um retorno de chamada.

Na verdade, a razão pela qual você obtém uma exceção aqui é o máximo que evita forçar a opinião. Mocha não tem uma opinião sobre se a promessa devolvida ou o retorno de chamada são oficiais. Você não pode ter os dois ao mesmo tempo, pois isso leva a resultados ambíguos. Resultados ambíguos em uma estrutura de teste devem ser considerados um erro. Daí a mensagem de erro para ajudá-lo a estar ciente disso e fazer a escolha e alteração de acordo com sua opinião.

Pode ser benéfico diminuir a quantidade de drama. "você quebrou o Mocha" não está ajudando ninguém. Um aumento de versão sempre maior é explicitamente definido como alterações significativas que podem exigir que você ajuste seu código. Você pode permanecer no 2.x para ter tempo de fazer as alterações para corrigir seus testes. Isso é uma evolução, não uma ruptura

@Munter Ainda não

Se você está sentindo drama, quero dizer nenhum. "você quebrou Mocha" não significa ser hiperbólico. Só acho que isso vai contra o design do módulo e quebra o contrato original da API.

Como meintoned antes do babel async / await não funciona bem com o novo mocha @ 3. Exemplo:

it('should fill userName and password', async function (done) {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    try {
      assert.equal(userNameVal, 'admin');
      assert.equal(passwordVal, 'changeme');
    } catch (error) {
      done(error);
      return;
    }

    done();
  });

Este código funciona bem com mocha @ 2, mas não funciona com mocha @ 3 porque em await ele retorna uma promessa

Acho que este PR é relevante aqui => https://github.com/mochajs/mocha/pull/2413
Mais complexidade para lidar com os casos extremos desse erro.

@artyomtrityak Esse é um ótimo exemplo de onde done é desnecessário.

Os detratores disseram sua parte. No entanto, os mantenedores do Mocha discordam do (s) argumento (s) para reverter essa mudança. Eran Hammer disse (parafraseado): "Como mantenedor, uma das coisas mais difíceis que você pode fazer é desapontar aqueles que querem mover o trabalho em sua direção."

Sou bem-vindo a soluções alternativas - mais documentação (por exemplo, mais exemplos deste erro e como corrigi-los), melhores mensagens de erro - mas não estou interessado em drama, grosseria ou reclamação. Contribuir com qualquer uma dessas soluções alternativas para o Mocha ajudaria a transformar um negativo em positivo.

Se você não gosta dessa mudança e simplesmente não consegue ser construtivo sobre ela, é o OSS - você pode bifurcar o projeto e reverter as mudanças lá.

@boneskull ele se transforma em funções assíncronas que retornam promessas, eu não preciso terminar meu caso de teste quando a promessa será cumprida, mas preciso fazer algumas verificações personalizadas em torno dos resultados. Como eu disse, este código funciona perfeitamente com o mocha @ 2, mas com o mocha @ 3 não. Portanto, minha equipe (~ 20ppl) não pode passar para o último mocha por causa disso.

Atualmente o mocha 2.x dá muita flexibilidade, há algum motivo técnico para essa mudança?

@artyomtrityak Esse é um ótimo exemplo de onde isso é desnecessário.

Você pode dar um exemplo de como isso deve ficar com a sintaxe de babel async/await e sem return new Promise ?

@artyomtrityak Que tal abraçar as promessas totalmente? Então você pode encurtar seu teste para:

it('should fill userName and password', async function () {
    const userNameField = global.driver.wait(until.elementLocated({css: '#username'}));
    userNameField.sendKeys('admin');

    const passNameField = global.driver.findElement({css: '#password'});
    passNameField.sendKeys('*******');

    const userNameVal = await userNameField.getAttribute('value');
    const passwordVal = await passNameField.getAttribute('value');

    assert.equal(userNameVal, 'admin');
    assert.equal(passwordVal, 'changeme');
  });

Se você se sente pessoalmente atacado, acho que precisa reconsiderar o quanto está emocionalmente envolvido nessa conversa. Nenhum dos comentários foi pessoal. Tenho certeza de que todos vocês são ótimos, especialmente considerando que doaram seu tempo para ajudar a manter este projeto, que é muito apreciado e muito recomendável.

A maioria de vocês (os mantenedores ativos atualmente) começaram a trabalhar no Mocha por volta de meados de 2014. O Mocha já estava estabelecido no ponto em que vocês começaram a contribuir. É apenas minha opinião, mas não acho que estou sozinho em pensar que não se deve fazer alterações significativas em uma biblioteca estabelecida, a menos que seja justificado. Embora eu possa imaginar a justificativa original para essa mudança, ela não se sustenta bem quando alguém aponta o seguinte. Pedir um retorno de chamada comunica uma intenção clara. As promessas não são tão claras porque não são solicitadas, são devolvidas, o que pode acontecer de forma indireta e acidental (devolvidas de uma biblioteca de terceiros, por exemplo). Por causa dessas diferenças, as duas maneiras de ceder não são iguais e, portanto, tentar usar as duas não é realmente ambíguo. Os retornos de chamada devem ser escritos nos argumentos de teste. Você não pode fazer isso com promessas e, portanto, ao pedir um retorno de chamada, você comunicou suas intenções explicitamente. Sua comunidade levantou essas preocupações e, em vez de reconhecer o erro que vocês estão cometendo. Parece que você está até considerando forçar os testes a serem assíncronos para garantir que esse erro atue de forma consistente. Consulte => https://github.com/mochajs/mocha/pull/2413. Parece uma grande mudança para uma mensagem de erro que protege contra um erro improvável.

Vocês têm feito um ótimo trabalho mantendo esta biblioteca desde a saída de @tj , podem por favor pensar um pouco mais sobre essa mudança. Minha preocupação é que isso possa comprometer a biblioteca.

Concordo totalmente com @RobertWHurst.

Solicitar done deve substituir o comportamento da promessa retornada. Não é provável que solicite done quando não é necessário, e cenários de eventos emitidos em uma função async são um exemplo perfeito.

Do meu comentário acima:

it('fires change event when calling blah.doSomethingThatFiresChange', async function (done) {
  const blah = await getBlah()
  blah.on('change', () => done())
  blah.doSomethingThatFiresChange()
})

Conforme mais pessoas mudam para ES6 / 7 + async / await , isso se tornará um problema comum ao usar o Mocha.

Por favor, reconsidere esta mudança.

@RobertWHurst Você argumenta que definir um retorno de chamada done é uma intenção explícita. Uma declaração return não é uma intenção explícita? Ambos são definidos em seu código por você. Como podemos decidir que uma parte do seu código é intencional e outra não? Se você imaginar um mundo antes de () => foo qualquer instrução de retorno sempre teria sido explícita. A única razão pela qual você está todo irritado agora é porque você começou a usar declarações de retorno implícitas, pelo que eu só posso pensar que são razões estéticas.

Dado que muito do uso do Mocha é feito por iniciantes que geralmente copiam / colam exemplos, que muito provavelmente contêm um retorno de chamada done , como você lidaria com o caso em que esse novo usuário explicitamente retorna uma promessa, mas obtém um tempo limite? Este é o resultado se a mudança pela qual você está louco for revertida.

O comportamento atual é muito mais claro sobre o que está errado do que apenas um tempo limite inesperado

@Munter Com async funções na imagem, eu acho que a promessa retornada pontua mais baixo na escala de explicitação porque é criada e retornada automaticamente, quer você use ou não await :

it('should work with a async function that could have been sync', async function () {
  assert.ok(true);
});

it('should work with a legitimately async function', async function () {
  assert.equal(await getBlah(), 'blah');
});

it('should work with a fully spelled-out Promise-based test', function () {
  return getBlah().then(function (blah) {
    assert.equal(blah, 'blah');
  });
});

E então há o polêmico:

it('should foo', async function (done) {
  assert.equal('foo', 'foo');
  done();
});

Também é fácil para aquele pequenino async entrar por acaso, então devemos pensar no (pelo menos) primeiro exemplo como um novo tipo de pegadinha, como @elado aponta.

Após um pouco de conversa aqui => https://github.com/mochajs/mocha/pull/1320, tive uma ideia para uma solução alternativa para o problema. Eu adicionei um PR para seu prazer de revisão aqui => https://github.com/mochajs/mocha/pull/2454

: cervejas:

Você argumenta que definir um retorno de chamada concluído é uma intenção explícita. Uma declaração de retorno não é uma intenção explícita?

@Munter Não se esqueça de que as expressões de função de seta de coffeescript e es6 retornam _implicitamente_, então você pode fazer algo como

it 'should foo', (done) -> request('/foo').end (err, res, body) -> done()

e acho que você está seguro. Mas este problema significa que você tem que transformar aquele belo one-liner em algo como

it 'should foo', (done) ->
  request('/foo').end (err, res, body) -> done()
  return

Este é exatamente o nosso problema com nossa base de código geral em cada um e em mecano . Considere isto:

it 'should foo', (done) ->
  obj =
    run: ->
      done()
      @
    then: -> "what the hell, i'm not a promise"
  obj.run()

Isso não é totalmente específico para o coffeescript, mas retornar valores implícitos o torna pior. O Mocha deve detectar instâncias de promessa válidas. Além disso, talvez uma opção possa desativar esse recurso.

Olá a todos,
Encontrou esse problema com o código a seguir;

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', (done) ->
      chai.request(app).get('/').end((err, res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
        done()
      )
    )
  )
)

Resolvi isso respeitando a cadeia de promessas e omitindo o retorno de chamada done() .

describe('Page', ->
  describe('/GET home', ->
    it('it SHOULD return the homepage', ->
      chai.request(app).get('/').then((res) ->
        res.should.have.status(200)
        res.text.should.be.a('string')
      )
    )
  )
)

Espero que isso ajude outras pessoas, que se deparam com um erro semelhante: sorria:

PS : coração: Mocha btw: +1:

EDIT Remova catch() base no comentário de @Munter .

@karlbateman Obrigado. Você não precisa desse último .catch embora. Uma promessa rejeitada é contada como um erro pelo Mocha

@Munter entendo, obrigado: smiley:

Ao pensar um pouco mais eu gosto muito do comportamento de esperar tanto a promessa quanto o retorno. Algum progresso foi feito com a implementação disso?

@ light24bulbs Obrigado pelo feedback! Não, afaik ninguém começou a trabalhar nisso, já que é apenas uma ideia que coloquei lá para ver as reações. Eu estava verificando agora se houve algum feedback adicional sobre a ideia, algum caso em que ela não funciona, etc.

Existe uma solução alternativa para isso ao usar o babel?

Existe uma solução alternativa para isso ao usar o babel?

Eu embrulho duas vezes:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

@SaschaNaz obrigado, isso funciona na v3.2.0 :)

6 meses depois, este problema ainda está travando todos os testes js modernos ...
vergonha

@benfavre Obrigado pelas palavras de incentivo que certamente

@Munter Não se preocupe, fico feliz por ajudar a identificar um problema específico que enfrentei como um novo usuário de mochajs.
@SaschaNaz sugeriu que a solução de embrulhar duas vezes não ajudou.

=> Usar promessas funcionou exclusivamente como deveria.

Da próxima vez, acho que devo apenas "+1" como um Morron para não ser insultado de graça.
Não houve nada de insultante em minha mensagem, além disso, minha declaração permanece verdadeira.

A maioria das pessoas simplesmente escolherá outro framework ... a partir de agora, está simplesmente quebrado com async / await, sem indicações claras em qualquer lugar no site principal e nenhuma mensagem de erro clara no cli.

Feliz Ano Novo e divirta-se brincando com as crianças.

Calafrio...

As discussões evoluíram para "prestar atenção" ao retorno de chamada done e ao retorno de Promise : https://github.com/mochajs/mocha/issues/2509

Fechando. veja # 2509

Caso algum corpo ainda esteja lutando ...

Existe uma solução alternativa para isso ao usar o babel?

Agora, com o manipulador integrado para promessa conforme menção em # 2509, não temos que usar wrapping twice hack assim:

it("should work", done => {
  (async () => {
    await something;
    done();
  })();
});

Em vez disso, podemos simplesmente seguir com isto:

it("should work", async () => {
  await something;
});

Verifique esta postagem para mais detalhes

@ lo-tp
1.) este código funciona quando é compilado com o babel?

it("should work", async () => {
  await something;
});

@SerkanSipahi

  • Sim, para usar a sintaxe async/await agora, precisamos da ajuda de babel.

Não tenho certeza se estou faltando alguma coisa ... mas resolvi esse problema não usando nenhuma instrução de retorno com minhas promessas e apenas contando com done ().

Aqui está um exemplo que funciona para mim

  describe('STRIPE CHARGES: Get Charges', () => {
    it('should list customer charges', async () => {
      const charges = await Stripe.getChargesList(customerObj);
      charges.data[0].should.have.property('id');
      charges.data[0].amount.should.have.property('id');
    });
  });

Estou usando o mocha 4 com chai.assert.

No início, tentei usar o callback done () dessa forma.

it('should throw an error', async function(done) {
  try {
    result = await something('value that causes something() to throw an Error');
    done('failed');
  } catch (e) {
    done();
  }
});

Falhou com o

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both."

Esse erro é o que me trouxe aqui, a esta página. Depois de passar por esse longo segmento (e admito que não entendi totalmente todas as disputas), devo entender que não devo usar done () com chamadas assíncronas porque elas são baseadas em promessas?

Em caso afirmativo, como faço para passar no teste a seguir quando a chamada para esperar algo () gera um erro e isso é o que eu esperava que acontecesse?

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
  } catch (e) {
    // how to indicate that this should happen, if I can't call done() to tell mocha?
  }
});

Alguém poderia me ajudar a entender esse caso específico por favor? Devo usar a biblioteca de asserções ou há algo adicional que preciso inserir no código acima?

Muito Obrigado.

A solução de baixo nível e sujeita a erros:

it('should throw an error', async function() {
  try {
    result = await something('value that causes something() to throw an Error');
    throw new Error('Promise unexpectedly fulfilled');
  } catch (e) {
    // Optionally assert something about e...
  }
});

Eu usaria uma biblioteca de asserções em qualquer dia da semana:

const expect = require('unexpected');
it('should throw an error', async function () {
    await expect(something(), 'to be rejected with', 'the error message');
});

Ou chai + chai-como-prometido:

const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;

it('should throw an error', async function () {
    await expect(something()).to.be.rejectedWith('argh');
});

Olá pessoal, este funciona bem com asyncawait (apenas omita 'done ()')

describe('New order from landing', function() {
    describe('check new client function', function () {
        it("must check db and return that random client is new", async function () {
            var result = await handler.checkClient(reqNewClient.body)
                expect(result).to.have.property('newclient').equal(true)
            })
    })
})

resultado:

Novo pedido de desembarque
verificar a nova função do cliente
√ deve verificar db e retornar que o cliente aleatório é novo
1 passagem (9 ms)

@kolykhalov Obrigado Funcionou para mim

No meu caso, envolvi o bloco assíncrono em um try / catch

it('It should activate a user.', async() => {
        return new Promise((resolve, reject) => {
            try {
                // Get the last activation token from the DB
                let sql = 'select * from activation_tokens order by created_at desc limit 1';
                let res = await DB.query(sql);
                let {token} = res[0];

                server
                    .post('/auth/activate')
                    .send({token})
                    .set('Content-Type', 'application/x-www-form-urlencoded')
                    .expect('Content-Type', /json/)
                    .expect(200)
                    .end((err, res) => {
                        res.body.data.should.contain.keys('message');
                        res.body.data.message.should.equal("Activated");
                        resolve();
                    });
            } catch (e) {
                console.error(e);
            }
        });
    });

Atualizado para evitar vulnerabilidades. Agora tenho que reescrever 99 testes. FML

Então, apenas para deixar claro qual é o problema, como alguns estão dizendo que você não deveria precisar usar done e async , aqui está um exemplo onde você gostaria de usar ambos.

it('should error on fn', async function(done) {
  try {
    await fn();
  } catch (e) {
    // assert some things here
    done()
  }
});

Não usar done deixará o teste passar se nenhum erro for gerado. Alguns sugeriram usar coisas como expect(await fn).to.throw('blah') , mas às vezes você precisa verificar mais propriedades do que cabem em uma linha.

Eu estava recebendo este erro, mas acidentalmente retornava uma promessa (superteste)

it('Should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Remover a cláusula de 'devolução' resolveu o problema

Para qualquer pessoa que ficou louca por isso ...

Isso funciona:

it("should work", async () => {
  await something;
});

Isso não:

it("should work", async (done) => {
  await something;
});

Você deve remover done como um parâmetro.

@tamoyal , yeah - como elado postou em 3 de agosto de 2016.

Usar async / await retorna um Promise implícito. O uso do retorno de chamada done é para
Assíncrono baseado em CPS , conforme mostrado aqui . Mocha suporta os dois ... mas não ao mesmo tempo.
Isso está documentado e tem sido o comportamento padrão desde o lançamento do Mocha-3.0.

Eu estava recebendo este erro, mas acidentalmente retornava uma promessa (superteste)

it('should do something', (done) => {
       return supertest(server)
           .get(`/api/endpoint`)
           .set('somekey', 'somevalue')
           .expect(200)
           .then(() => { done(); })
           .catch(done);
});

Remover a cláusula de 'devolução' resolveu o problema

@victorsferreira , parece que essa deveria ter sido a solução ...

it('should do something', () => {
  return supertest(server)
    .get(`/api/endpoint`)
    .set('somekey', 'somevalue')
    .expect(200);
});

@tamoyal Sim, foi isso que me fez tropeçar. É muito pouco intuitivo ter uma biblioteca de terceiros examinando os parâmetros de uma função que eu crio e usando isso para tomar uma decisão sem me dizer que o fez.

@mienaikoe , este cenário exato está explicitamente documentado , sabe ...

@plroebuck duas coisas:

1) Há uma diferença entre documentar algo e ser uma das poucas bibliotecas no npm que faz uma introspecção nos parâmetros de uma função. Eu poderia documentar que fiz uma verificação de antecedentes de todos os meus amigos quando os conheci, mas ainda é estranho e as pessoas ainda reclamariam, a menos que eu tivesse um motivo e explicitamente lhes contasse o motivo.

2) Há uma falha na documentação:

No Mocha v3.0.0 e mais recente, retornando uma promessa e chamando concluído () ...

Não se trata de chamar concluído, é sobre _especificar done como um parâmetro_ se você o usa ou não.

@mienaikoe

  1. O Mocha verifica o segundo parâmetro de it para ver se a função existe e (se houver) sua aridade para determinar se o usuário adicionou um retorno de chamada. Isso mal se qualifica como introspecção e é comumente usado em JavaScript.
  2. Envie PR para corrigir a documentação se você achar que está muito vaga.

Remover feito como parâmetro funcionou para mim!

ANTES:

image

DEPOIS (funciona!)

image

@QauseenMZ , não veja nenhuma diferença entre o seu código "antes" e "depois".

Você também não está retornando sua promessa. Não deveria ser mais parecido com o seguinte?

  it('should receive successful RPC response', async () => {
    return await getResponse(unitData)
      .then((res) => {
        expect(res).to.have.status(200);
      });
  });

PS. A ordem do argumento de retorno de chamada do seu Nó foi destruída ... error _sempre_ vai primeiro.

@plroebuck Obrigado por mencionar! Acabei de editar isso.

Não estou cumprindo a promessa porque a função getResponse está retornando um retorno de chamada. Foi a única maneira de fazer funcionar. A função getResponse é a seguinte:

image

Aqui, o erro é o segundo parâmetro apenas porque a função getResponse de retorno de chamada está retornando. Por favor, deixe-me saber sua opinião sobre isso. Obrigado!

Algumas partes parecem meio ilógicas. Apenas para maior clareza, qual pacote request você está usando?
Por que você precisaria retornar o objeto options (que você chamou de unitData )?

  • De onde obj vem?
  • Por que você teria um res.body.error com um res.statusCode === 200 ?

PS. Apenas cole o próprio código em vez de imagens do código ...

Para qualquer pessoa que ficou louca por isso ...

Isso funciona:

it("should work", async () => {
  await something;
});

Isso não:

it("should work", async (done) => {
  await something;
});

Você deve remover done como um parâmetro.

@tamoyal Você salvou minha vida <3

Isso quebra e resulta no mesmo erro:

it('Location Querying Works', async () => {
    await WeatherComponent.pullLocationData();
    Vue.nextTick(()=>{
      expect(WeatherComponent.$el.textContent).contain("Spain")
    })
  })

Para lhe dar uma solução rápida para esse problema, envolva todo o seu teste em uma promessa e use resolve como faria com done .

Vire isto:

it.only("Works with the database", async (done) => {
    let browser = await puppeteer.launch();
    let query = db.collection('lists').where('ownerId', '==', 'UJIXXwynaCj8oeuWfYa8');
    let ct = 0;
    query.onSnapshot(async querySnapshot => {
        if (ct === 0) {
            await addAPurpose(browser, session, sessionSig); // A function with a bunch of Puppeteer code.
            ct++
        } else {
            expect(querySnapshot.size).to.equal(1);
            expect(querySnapshot.docs[0].get().title).to.equal("Understand Mocha");
            done();
        }
        console.log(`Received query snapshot of size ${querySnapshot.size}`);
    }, err => {
        console.log(`Encountered error: ${err}`);
    });
});

nisso:

it.only("Works with the database", async () => {
    const onSnap = new Promise(async (res, rej) => {
        let browser = await puppeteer.launch();
        let query = db.collection('lists').where('ownerId', '==', 'UJIo1gGMueoubgfWfYa8');
        let ct = 0;
        let unsubscribe = query.onSnapshot(async querySnapshot => {
            if (ct === 0) {
                await addAPurpose(browser, session, sessionSig);
                ct++
            } else {
                expect(querySnapshot.size).to.equal(1);
                expect(querySnapshot.docs[0].data().title).to.equal("Evolution");
                // done(); 
                unsubscribe();
                res();
            }
            console.log(`Received query snapshot of size ${querySnapshot.size}`);
        }, err => {
            console.log(`Encountered error: ${err}`);
            rej()
        });
    });
    return onSnap;
});

E funciona como você deseja.

A necessidade de remover done me surpreendeu, pois o teste foi executado até terminar foi chamado. Para tornar mais óbvio que o done não deve ser usado com async, as funções async devem falhar _imediatamente_ se o done for passado. Mocha deve começar o teste por:

  1. Ver se a função é assíncrona e
  2. Detectando o argumento done .

Se os dois estiverem presentes, ele deve lançar em vez de permitir que meus testes sejam executados até que done seja chamado e, em seguida, me atire. Então, o erro deve sugerir que você envolva seu código em outra promessa e use resolve como faria com done .

Eu sei que você pode usar function.prototype.name === "AsyncFunction" . Então é

if (function.prototype.name === "AsyncFunction && arg1.name === "done") {
  throw new Error("Can't use done with an async function")
}

para implementar isso.

Eu estava quase SOL nisso. Eu preciso executar o código assíncrono E espera-se que o código não termine de ser executado (nunca), então TENHO que usar pronto. O hack-a-round irritante era envolver meu código de teste assíncrono em uma função assíncrona de auto-chamada, mas deixar a função it como uma função de sincronização.

Solução:

it("It shouldn't be like this", function (done) {
    ( async function(){
        var life = require('InfiniteLife');
        var asyncRes = await life.someCoolstuff();
        assert(asyncRes);
        setTimeout( function(){
            done();
        },letsCallItQuitsNow);
    })();
});

Um exemplo de funções assíncronas com quebra concluída.

it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
        let aObj = await admin.createAdmin();
        chai.request(server)
        .post("/authenticate")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: aObj.login,password:aObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            done();
        });
    });

Caso de Sucesso

it('If the credentials exists in the system it should return the token generated against it.', async () => {
        let adminObj = await admin.createAdmin();
        chai.request(server)
        .post("/auth/login")
        .set("Content-Type", "application/x-www-form-urlencoded")
        .send({username: adminObj.login,password:adminObj.password})
        .end((err, res) => {
            res.should.have.status(200);
            res.body.should.be.a("string");
            // done();
        });
    });

@ Haisum92

Você não precisa de done nem async em seu teste. Chai http retorna uma promessa. Os testes do Mocha funcionam com código assíncrono se você retornar uma promessa.

it('If the credentials exists in the system it should return the token generated against it.', () => {
  let adminObj = await admin.createAdmin();
  return chai.request(server)
    .post("/auth/login")
    .set("Content-Type", "application/x-www-form-urlencoded")
    .send({username: adminObj.login,password:adminObj.password})
    .end((err, res) => {
      res.should.have.status(200);
      res.body.should.be.a("string");
    });
});

@NateZimmer

Sua solução postada é para o tempo limite do teste se demorar muito. O Mocha já possui essa funcionalidade incorporada.

it("It should be liek this", async function () {
  this.timeout(letsCallItQuitsNow);

  var life = require('InfiniteLife');
  var asyncRes = await life.someCoolstuff();
  assert(asyncRes);
});

Seu exemplo também não parece falhar no teste se o tempo limite for excedido, então tome cuidado dependendo da exatidão dele

`Usando express / js e funções de nuvem do firebase, verifique as respostas de erro da API, obtenha um token e tente novamente. Certifique-se de que, se usar express, você retorne .json e não .send, também funciona com o logger bugs com mocha, portanto, use console.log simples.
Certifique-se de usar assíncrono e emitir feito completamente

  process.env.NODE_ENV='TESTING';
  const { FIREBASE_UID } = require('dotenv').config()?.parsed;
  const { red, green, white } = require('chalk');
  const chai = require('chai');
  const chaiHttp = require('chai-http');
  const server = require('../lib/api').API;
  const should = chai.should();
  const expect = chai.expect;
  chai.use(chaiHttp);
  const test = chai.request(server).keepOpen();

  const shouldPrint = (details, status) => {
      return white(`details: ${details}, status: ${status}`);
  };

describe('Authorization Middleware Error Check for bad token', () => {

    it(shouldPrint('Bad Request', red(400)), async () => {
        try {
            const res = await test.get('/').send()
            res.should.have.status(400);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Bad request')
            expect(status).to.equal(400)
        } catch (error) { 
            throw error 
        }
    })


    it(shouldPrint('Unauthorized', red(401)), async () => {
        try {
            const res = await test.get('/').set('Authorization', 'Bearer bad123token').send()
            res.should.exist
            res.should.have.status(401);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('Unauthorized')
            expect(status).to.equal(401) 
        } catch (error) {
            throw error
        }
    })

})

 describe('Get token and ping', () => {
    let adminToken

    it(shouldPrint( 'Create custom jwt for testing', green(200)), async () => {
        try {
            const res = await test.get(`/createToken/${FIREBASE_UID}`).send()
            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            adminToken = (JSON.parse(res.text)).token
        } catch (error) {
            throw error
        }
    })

    it(shouldPrint('PING', green(200)), async () => {
        try {
            const res = await test.get('/')
                .set('x-tenorders-testing', 'true')
                .set('Authorization', `Bearer ${adminToken}`).send()

            res.should.exist
            res.should.have.status(200);
            res.should.have.json
            const { details, status } = JSON.parse(res.text)
            expect(details).to.equal('PING')
            expect(status).to.equal(200)
        } catch (error) {
            throw error
        }   
    })

})
`

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

Questões relacionadas

eschwartz picture eschwartz  ·  3Comentários

niftylettuce picture niftylettuce  ·  3Comentários

CADBOT picture CADBOT  ·  3Comentários

luoxi001713 picture luoxi001713  ·  3Comentários

adamhooper picture adamhooper  ·  3Comentários