Jest: async / await toThrow não está funcionando

Criado em 15 set. 2016  ·  21Comentários  ·  Fonte: facebook/jest

próximo teste

async function check() {
  throw new Error('Test');
}

expect(async () => 
  await check()
).toThrow();

irá falhar e não detectará o erro corretamente (estou usando a versão mais recente do jest)

Comentários muito úteis

Ao invés de

expect(async () => await check()).toThrow()
// Jest error: "Expected the function to throw an error. But it didn't throw anything."

isso funciona:

expect(check()).rejects.toEqual(new Error('Test'))

Sim, a sintaxe é estranha ...

Todos 21 comentários

Sim, isso não é compatível atualmente. Veja https://github.com/facebook/jest/issues/1377

Ao invés de

expect(async () => await check()).toThrow()
// Jest error: "Expected the function to throw an error. But it didn't throw anything."

isso funciona:

expect(check()).rejects.toEqual(new Error('Test'))

Sim, a sintaxe é estranha ...

Você também pode escrever:

let error;
try {
  await check();
} catch (e) {
  error = e;
}
expect(error).toEqual(new Error('Test'));

O seguinte funcionou para mim também:

async function check() {
  throw new Error('Test');
}

expect(check()).rejects.toEqual(new Error('Test'))

Veja: https://facebook.github.io/jest/docs/en/tutorial-async.html#rejects

Adicionando à lista de soluções alternativas.
Se você gosta de mim, deseja detectar um tipo específico de erro e não se importa com a mensagem:

async function check() {
  throw new SomeSpecificError('Whatever');
}

await check()
  .then(() => {
    throw new Error('Should go to .catch, not enter .then');
  })
  .catch((err) => {
    expect(err).toBeInstanceOf(SomeSpecificError);
  });
await expect(check).rejects.toThrow(SomeSpecificError);

Deve funcionar bem

await expect(check).rejects.toThrow(/DUPLICATES_DETECTED/);

é o que eu faço.

async function check() {
  throw new Error("Test");
}
await expect(check).rejects.toThrow(Error);
expect(received).rejects.toThrow()

received value must be a Promise.
Received:
 function: [Function check]

Você precisa invocá-lo para dar a ele a promessa real

async function check() {
  throw new Error("Test");
}
await expect(check()).rejects.toThrow(Error);

Deve funcionar bem

it(`some test`, async () => {
  async function check() {
    try {
      return Promise.reject(await asyncMethod());
    } catch (error) {
      throw new Error("test");
    }
  }
  await expect(check()).rejects.toThrow(Error);
});

pode trabalhar

versão

  • brincadeira: 23.4.2
  • ts-jest: 23.1.2

Eu sei que é necessário uma promessa, mas o caso que você está oferecendo não é :)

Oi ! Posso estar fazendo algo errado, mas ainda tenho problemas com erros personalizados em chamadas assíncronas. Considere o seguinte:

class CustomErrorType {}
// ...
test('check throws custom error', async () => {            
    async function check() {
        throw new CustomErrorType();
    }
    await expect(check()).rejects.toThrow(CustomErrorType);
});

então o teste falha com a seguinte saída:

Esperava-se que a função gerasse um erro do tipo:
"CustomErrorType"
Mas não jogou nada.

Portanto, o teste falha - embora funcione perfeitamente quando a classe lançada é Error . Quando tento estender Error com
`` `javascript
classe CustomErrorType estende Error {}
`` ``

então o erro é

Esperava-se que a função gerasse um erro do tipo:
"CustomErrorType"
Em vez disso, ele jogou:
Erro

Alguma pista sobre o que há de errado nessa amostra? Estou usando o jest 23.4.2.

@Marchelune Quando você escreve class CustomErrorType extends Error {} você não definiu realmente uma nova classe. Se você der a CustomErrorType um corpo, mesmo apenas um construtor que não faz nada além de chamar super (), ele funcionará.

No caso de alguém lançar algo diferente da classe Error da instância e se esforçar para usar o conselho daquele thread (como eu fiz).
toThrow() verificará qual valor lançado é a instância da classe Error e, se não for, o lançamento não será detectado. Isso não era óbvio pelos documentos e pelo bom senso.
Isso irá falhar, embora claramente lance:

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toThrow()`

mas isso vai funcionar (não tenho certeza se existe uma maneira melhor):

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toBeTruthy()`

@Karabur typo toBeThruthy deve ser toBeTruthy

Aqui está um teste explícito que garante que ele definitivamente será interrompido se NÃO for acionado e corresponder a um erro específico.

yourCoolAsyncMethod('yeah it is')
    .then(ok => { // Presumes not a Promise<void>
        expect(ok).toBeUndefined();
        done();
    })
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

Olá @SimenB.
Se eu tiver isso e estiver funcionando.

async function check() {
    // do something
}
await expect(check()).rejects.toThrow(InternalServerErrorException);

InternalServerErrorException é lançada com um objeto ....

Como posso também declarar propriedades no erro lançado? Por exemplo:

  • exception.message = 'ERROR'
  • exception.status = '500'

Aqui está um teste explícito que garante que ele definitivamente será interrompido se NÃO for acionado e corresponder a um erro específico.

yourCoolAsyncMethod('yeah it is')
    .then(ok => {
        expect(ok).toBeUndefined();
        done();
    })
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

O problema disso é que seu teste será aprovado se o código nunca tiver erros.

Portanto, esse teste realmente não ofereceria muito valor, pois os resultados esperados variam com base em se o código que está testando tem um erro ou não.

Os testes devem ter um fluxo negativo ou positivo e devemos testar apenas o que é necessário para testar. Esse tipo de aleatoriedade não é o maior.

Olá, @ David-Tennant 🖐

Obrigado por apontar isso. Eu estava trabalhando em um método Promise<notVoid> naquela época. Percebi que não compartilho uma solução comum que também seja válida para Promise<void> . Atualizei minha resposta com um comentário que diz que fiz suposições. Eu concordo com você sobre o fluxo.

Obrigado 💨

Olá @futuredayv 👋

Olá, @ David-Tennant 🖐

Obrigado por apontar isso. Eu estava trabalhando em um método Promise<notVoid> naquela época. Percebi que não compartilho uma solução comum que também seja válida para Promise<void> . Atualizei minha resposta com um comentário que diz que fiz suposições. Eu concordo com você sobre o fluxo.

Obrigado 💨

O problema é que seu teste ainda passará se o teste retornar inválido. Qual deve ser um bug, certo?
Chamar done() significa "Meu teste foi aprovado da maneira esperada e estou pronto para passar para o próximo teste"

yourCoolAsyncMethod('yeah it is')
     // -- Lets say your method does not throw, and returns void instead
    .then(ok => { // Presumes not a Promise<void>
        expect(ok).toBeUndefined(); // -- At this point if it was void, this would pass
        done(); // -- Then your test would end and the catch would not be reached
        // -- Now you have unknown behavior passing a test where it's suppose to catch a fail
    })
    .catch(bad => {
        // -- This block does not run because done() is called above
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

Não tenho certeza de qual seria a solução para o seu caso de uso. Mas eu começaria não chamando done() no then ou talvez lançando um erro no "então" para ter certeza de que o teste falhou,

yourCoolAsyncMethod('yeah it is')
    .then(ok => throw new Error("This block of code should not be reached")
    .catch(bad => {
        expect(bad).toBeDefined();
        expect(bad).toMatchInlineSnapshot(
            `Specifically serious error!`
        );

        done();
    });

No caso de alguém lançar algo diferente da classe Error da instância e se esforçar para usar o conselho daquele thread (como eu fiz).
toThrow() verificará qual valor lançado é a instância da classe Error e, se não for, o lançamento não será detectado. Isso não era óbvio pelos documentos e pelo bom senso.
Isso irá falhar, embora claramente lance:

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toThrow()`

mas isso vai funcionar (não tenho certeza se existe uma maneira melhor):

async function f() {throw 'aa'}
const res = await expect(f()).rejects.toBeTruthy()`

Uma maneira ligeiramente melhor é usar toBeDefined() vez de toBeTruthy() :
async function f() {throw 'aa'} const res = await expect(f()).rejects.toBeTruthy()`

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