Como testar o tipo de exceção lançada no Jest


161

Estou trabalhando com algum código no qual preciso testar o tipo de exceção gerada pela função (É TypeError, ReferenceError etc.).

Minha estrutura de teste atual é o AVA e posso testá-lo como um segundo t.throwsmétodo de argumento , como aqui:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  t.is(error.message, 'UNKNOWN ERROR');
});

Comecei a reescrever meus testes no Jest e não conseguia descobrir como fazer isso facilmente. Isso é possível?

Respostas:


225

No Jest, você deve passar uma função para expect (function) .toThrow (em branco ou tipo de erro).

Exemplo:

test("Test description", () => {
  const t = () => {
    throw new TypeError();
  };
  expect(t).toThrow(TypeError);
});

Se você precisar testar uma função existente, seja ela lançada com um conjunto de argumentos, precisará envolvê-la em uma função anônima em expect ().

Exemplo:

test("Test description", () => {
  expect(() => {http.get(yourUrl, yourCallbackFn)}).toThrow(TypeError);
});

79

Um pouco estranho, mas funciona e imho é de fácil leitura:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  try {
      throwError();
      // Fail test if above expression doesn't throw anything.
      expect(true).toBe(false);
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

Catchbloquear pegar sua exceção, então você pode testar no seu raise Error. Estranho expect(true).toBe(false);é necessário para falhar no teste, se o esperado Errornão for lançado. Caso contrário, essa linha nunca será alcançável ( Errordeve ser aumentada antes deles).

EDIT: @Kenny Body sugere uma solução melhor que melhore a qualidade do código se você usar expect.assertions()

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', () => {
  expect.assertions(1);
  try {
      throwError();
  } catch (e) {
      expect(e.message).toBe("UNKNOWN ERROR");
  }
});

Veja a resposta original com mais explicações: Como testar o tipo de exceção lançada no Jest


18
Esta é uma maneira muito detalhado de testes para exceções quando Jest já tem o caminho expect.toThrow () de verificação de exceções: jestjs.io/docs/en/expect.html#tothrowerror
gomisha

4
Sim, mas ele testa apenas o tipo, não a mensagem ou outro conteúdo, e a pergunta era sobre mensagem de teste, não o tipo.
bodolsog

2
Hah. Realmente parecido com este, pois meu código precisa testar um valor do erro gerado, então eu preciso da instância. Eu ia escrever a expectativa defeituosa como expect('here').not.toBe('here');apenas para a diversão :-)
Valery

4
@ Valery ou: expect('to be').not.toBe('to be')no estilo Shakespeare.
Michiel van der Blonk 28/10

2
resposta mais subestimada!
Edwin Ikechukwu Okonkwo

41

Eu uso uma versão um pouco mais concisa:

expect(() => {
  //code block that should throw error
}).toThrow(TypeError) //or .toThrow('expectedErrorMessage')

2
Curto e preciso.
flapjack

33

Pela minha exposição (ainda que limitada) ao Jest, achei expect().toThrow()adequado se você deseja SOMENTE testar um erro de um tipo específico:

expect(() => functionUnderTest()).toThrow(TypeError);

OU um erro é gerado com uma mensagem específica:

expect(() => functionUnderTest()).toThrow('Something bad happened!');

Se você tentar fazer as duas coisas, receberá um falso positivo. Por exemplo, se seu código for lançado RangeError('Something bad happened!'), este teste será aprovado:

expect(() => functionUnderTest()).toThrow(new TypeError('Something bad happened!'));

A resposta do bodolsog que sugere o uso de uma tentativa / captura está próxima, mas, em vez de esperar que true seja falso para garantir que as asserções esperadas na captura sejam atingidas, você pode usar expect.assertions(2)no início do seu teste, onde 2está o número de asserções esperadas . Eu sinto que isso descreve com mais precisão a intenção do teste.

Exemplo completo de teste do tipo E mensagem de um erro:

describe('functionUnderTest', () => {
    it('should throw a specific type of error.', () => {
        expect.assertions(2);

        try {
            functionUnderTest();
        } catch (error) {
            expect(error).toBeInstanceOf(TypeError);
            expect(error).toHaveProperty('message', 'Something bad happened!');
        }
    }); 
});

Se functionUnderTest()NÃO gerar um erro, as afirmações serão atingidas, mas oexpect.assertions(2) falha e o teste falharão.


D'oh. Eu sempre esqueço o recurso esperado de várias asserções do Jest (possivelmente eu pessoalmente não acho o mais intutivo, mas definitivamente funciona para esses casos!) Felicidades!
precisa saber é

Isso funcionou perfeitamente bem para mim. Isso deve ser usado.
Ankit Tanna

expect.hasAssertions()pode ser uma alternativa melhor quando o teste não possui asserções externas catch, porque você não precisa atualizar o número se adicionar / remover asserções.
André Sassi

12

Ainda não tentei, mas eu sugeriria usar a declaração toThrow de Jest . Então, acho que seu exemplo seria algo como isto:

it('should throw Error with message \'UNKNOWN ERROR\' when no params were passed', (t) => {
  const error = t.throws(() => {
    throwError();
  }, TypeError);

  expect(t).toThrowError('UNKNOWN ERROR');
  //or
  expect(t).toThrowError(TypeError);
});

Novamente, não teste, mas acho que deve funcionar.


8

O Jest tem um método toThrow(error)para testar se uma função é lançada quando é chamada.

Portanto, no seu caso, você deve chamar assim:

expect(t).toThrowError(TypeError);

Os documentos


1
Não funcionaria para o caso: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });se o método zombado createnão for async.
Sergey

7

O gracejo moderno permite que você faça mais verificações no valor rejeitado. Por exemplo:

const request = Promise.reject({statusCode: 404})
await expect(request).rejects.toMatchObject({ statusCode: 500 });

falhará com erro

Error: expect(received).rejects.toMatchObject(expected)

- Expected
+ Received

  Object {
-   "statusCode": 500,
+   "statusCode": 404,
  }

6

A documentação é clara sobre como fazer isso. Digamos que eu tenho uma função que aceita dois parâmetros e gera um erro se um deles for null.

function concatStr(str1, str2) {
  const isStr1 = str1 === null
  const isStr2 = str2 === null
  if(isStr1 || isStr2) {
    throw "Parameters can't be null"
  }
  ... // continue your code

Seu teste

describe("errors", () => {
  it("should error if any is null", () => {
    // notice that the expect has a function that returns the function under test
    expect(() => concatStr(null, "test")).toThrow()
  })
})

4

Caso você esteja trabalhando com Promises:

await expect(Promise.reject(new HttpException('Error message', 402)))
  .rejects.toThrowError(HttpException);

Isso é super útil, obrigado por economizar meu tempo !!
Aditya Kresna Permana

3

Acabei escrevendo um método de conveniência para a nossa biblioteca test-utils

/**
 *  Utility method to test for a specific error class and message in Jest
 * @param {fn, expectedErrorClass, expectedErrorMessage }
 * @example   failTest({
      fn: () => {
        return new MyObject({
          param: 'stuff'
        })
      },
      expectedErrorClass: MyError,
      expectedErrorMessage: 'stuff not yet implemented'
    })
 */
  failTest: ({ fn, expectedErrorClass, expectedErrorMessage }) => {
    try {
      fn()
      expect(true).toBeFalsy()
    } catch (err) {
      let isExpectedErr = err instanceof expectedErrorClass
      expect(isExpectedErr).toBeTruthy()
      expect(err.message).toBe(expectedErrorMessage)
    }
  }

O mesmo pode ser feito usando os próprios recursos do Jests. Veja a minha resposta para como isso pode ser feito - stackoverflow.com/a/58103698/3361387
Kenny corpo

3

Além do post de Peter Danis, apenas queria enfatizar a parte de sua solução envolvendo "[passar] uma função para expect (function) .toThrow (espaço em branco ou tipo de erro)".

No Jest, quando você testa um caso em que um erro deve ser gerado, dentro do seu wrapper expect () da função em teste, você precisa fornecer uma camada adicional de wrapper da função de seta para que ele funcione. Ou seja,

Errado (mas a abordagem lógica da maioria das pessoas):

expect(functionUnderTesting();).toThrow(ErrorTypeOrErrorMessage);

Certo:

expect(() => { functionUnderTesting(); }).toThrow(ErrorTypeOrErrorMessage);

É muito estranho, mas deve executar os testes com êxito.


1

experimentar
expect(t).rejects.toThrow()


4
Por que try? não há tentativa - mas resposta. Se esta for a resposta, por favor, elabore mais. o que você está adicionando à resposta existente?
dWinder 16/05/19

7
Acho que o @Razim estava dizendo que você deveria tentar a solução, não usar uma tentativa.
Tom
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.