Cómo probar el tipo de excepción lanzada en Jest


161

Estoy trabajando con algún código donde necesito probar el tipo de excepción lanzada por función (¿Es TypeError, ReferenceError, etc.).

Mi marco de prueba actual es AVA y puedo probarlo como un segundo t.throwsmétodo de argumento , como aquí:

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

Comencé a reescribir mis pruebas a Jest y no pude encontrar cómo hacerlo fácilmente. ¿Es posible?

Respuestas:


225

En Jest, debe pasar una función a esperar (función) .toThrow (en blanco o tipo de error).

Ejemplo:

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

Si necesita probar una función existente si se lanza con un conjunto de argumentos, debe envolverla dentro de una función anónima en expect ().

Ejemplo:

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

79

Un poco raro, pero funciona y en mi humilde opinión es bueno legible:

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");
  }
});

Catchbloque catch su excepción, entonces se puede probar en su elevada Error. expect(true).toBe(false);Se necesita extraño para reprobar su prueba si no se espera que Errorse espere . De lo contrario, esta línea nunca es accesible ( Errordebe aparecer antes que ellos).

EDITAR: @Kenny Body sugiere una mejor solución que mejora la calidad del código si usa 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");
  }
});

Vea la respuesta original con más explicaciones: Cómo probar el tipo de excepción lanzada en Jest


18
Esta es una forma muy detallada de probar excepciones cuando Jest ya tiene la forma expect.toThrow () de verificar excepciones: jestjs.io/docs/en/expect.html#tothrowerror
gomisha

44
Sí, pero solo prueba el tipo, no el mensaje u otro contenido y la pregunta era sobre el mensaje de prueba, no el tipo.
bodolsog

2
Jaja Realmente me gusta este, ya que mi código necesita probar un valor del error arrojado, así que necesito la instancia. Escribiría la expectativa defectuosa como expect('here').not.toBe('here');por diversión :-)
Valery

44
@Valery o: expect('to be').not.toBe('to be')en estilo Shakespeare.
Michiel van der Blonk

2
respuesta más subestimada!
Edwin Ikechukwu Okonkwo

41

Yo uso una versión un poco más concisa:

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

2
Corto y preciso.
flapjack

33

Por mi exposición (aunque limitada) a Jest, he encontrado que expect().toThrow()es adecuado si solo desea probar que un error se arroja de un tipo específico:

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

O se produce un error con un mensaje específico:

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

Si intentas hacer ambas cosas, obtendrás un falso positivo. Por ejemplo, si arroja su código RangeError('Something bad happened!'), esta prueba pasará:

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

La respuesta de bodolsog que sugiere el uso de un try / catch es cercana, pero en lugar de esperar que true sea falso para garantizar que las afirmaciones esperadas en el catch sean acertadas, puede usar expect.assertions(2)al comienzo de su prueba dónde 2está el número de afirmaciones esperadas . Siento que esto describe con mayor precisión la intención de la prueba.

Ejemplo completo de prueba del tipo Y mensaje de error:

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

Si functionUnderTest()NO arroja un error, las aserciones serán acertadas pero expect.assertions(2)fallará y la prueba fallará.


D'oh Siempre me olvido de la función de afirmaciones múltiples de Jest (posiblemente no me parece personalmente la más intuitiva, ¡pero definitivamente funciona para tales casos!) ¡Salud!
kpollock

Esto funcionó perfectamente bien para mí. Esto debería ser usado.
Ankit Tanna

expect.hasAssertions()podría ser una mejor alternativa cuando la prueba no tiene ninguna afirmación fuera catch, porque no tiene que actualizar el número si agrega / elimina afirmaciones.
André Sassi

12

No lo he intentado yo mismo, pero sugeriría usar la afirmación de Jest para lanzar . Así que supongo que su ejemplo se vería así:

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);
});

De nuevo, no lo he probado, pero creo que debería funcionar.


8

Jest tiene un método toThrow(error)para probar que una función se lanza cuando se llama.

Entonces, en su caso, debería llamarlo así:

expect(t).toThrowError(TypeError);

Los documentos


1
No funcionaría para el caso: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });si el método burlado createno lo es async.
Sergey

7

La broma moderna le permite hacer más controles sobre el valor rechazado. Por ejemplo:

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

fallará con error

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

- Expected
+ Received

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

6

La documentación es clara sobre cómo hacer esto. Digamos que tengo una función que toma dos parámetros y arrojará un error si uno de ellos es 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

Su prueba

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

En caso de que esté trabajando con Promises:

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

Esto es súper útil, ¡gracias por ahorrarme tiempo!
Aditya Kresna Permana

3

Terminé escribiendo un método de conveniencia para nuestra 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)
    }
  }

Lo mismo se puede hacer usando las propias características de Jests. Vea mi respuesta sobre cómo se puede hacer esto - stackoverflow.com/a/58103698/3361387
Kenny Body

3

Además de la publicación de Peter Danis, solo quería enfatizar la parte de su solución que involucra "[pasar] una función a esperar (función) .toThrow (en blanco o tipo de error)".

En Jest, cuando prueba un caso en el que debe arrojarse un error, dentro de su ajuste de la función que se espera (), debe proporcionar una capa adicional de ajuste de la función de flecha para que funcione. Es decir

Incorrecto (pero el enfoque lógico de la mayoría de las personas):

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

Correcto:

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

Es muy extraño, pero debería hacer que la prueba se ejecute con éxito.


1

tratar
expect(t).rejects.toThrow()


44
¿Por qué try? no hay prueba, sino respuesta. Si esta es la respuesta, por favor elabore más. ¿Qué agregas a la respuesta existente?
dWinder

77
Creo que @Razim estaba diciendo que deberías probar la solución, no usar un try catch.
Tom
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.