Come testare il tipo di eccezione generata in Jest


161

Sto lavorando con un codice in cui devo testare il tipo di eccezione generata dalla funzione (è TypeError, ReferenceError ecc.).

Il mio attuale framework di test è AVA e posso testarlo come secondo t.throwsmetodo di argomento , come qui:

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

Ho iniziato a riscrivere i miei test su Jest e non sono riuscito a trovare come farlo facilmente. È anche possibile?

Risposte:


225

In Jest devi passare una funzione in wait (funzione) .toThrow (vuoto o tipo di errore).

Esempio:

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

Se è necessario testare una funzione esistente se viene generata con una serie di argomenti, è necessario racchiuderla in una funzione anonima in awa ().

Esempio:

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

79

Un po 'strano, ma funziona e imho è ben leggibile:

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

Catchblock cattura la tua eccezione, quindi puoi provare il tuo rilancio Error. Strano expect(true).toBe(false);è necessario per fallire il test se previsto Errornon verrà lanciato. Altrimenti, questa linea non è mai raggiungibile ( Errordovrebbe essere sollevata prima di loro).

EDIT: @Kenny Body suggerisce una soluzione migliore che migliora la qualità del codice se si utilizza 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");
  }
});

Vedi la risposta originale con ulteriori spiegazioni: Come testare il tipo di eccezione generata in Jest


18
Questo è un modo molto dettagliato di testare le eccezioni quando Jest ha già il modo awa.toThrow () per verificare le eccezioni: jestjs.io/docs/en/expect.html#tothrowerror
gomisha,

4
Sì, ma verifica solo il tipo, non il messaggio o altro contenuto e la domanda riguardava il messaggio di prova, non il tipo.
bodolsog,

2
Hah. Mi piace davvero questo dato che il mio codice deve testare il valore dell'errore generato, quindi ho bisogno dell'istanza. Scriverei l'attesa difettosa come expect('here').not.toBe('here');solo per divertimento :-)
Valery,

4
@Valery o: expect('to be').not.toBe('to be')in stile Shakespeare.
Michiel van der Blonk,

2
risposta più sottovalutata!
Edwin Ikechukwu Okonkwo,

42

Uso una versione leggermente più concisa:

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

2
Corto e preciso
Flapjack,

33

Dalla mia (anche se limitata) esposizione a Jest, ho scoperto che expect().toThrow()è adatto se si desidera SOLO testare un errore di un tipo specifico:

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

O viene generato un errore con un messaggio specifico:

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

Se provi a fare entrambe le cose, otterrai un falso positivo. Ad esempio, se viene generato il codice RangeError('Something bad happened!'), questo test passerà:

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

La risposta di bodolsog che suggerisce l'utilizzo di un tentativo / cattura è vicina, ma invece di aspettarsi che il vero sia falso per garantire che le asserzioni attese nella cattura siano colpite, puoi invece utilizzare expect.assertions(2)all'inizio del test dove si 2trova il numero di asserzioni attese . Sento che questo descrive più accuratamente l'intenzione del test.

Esempio completo di test del tipo AND messaggio di un errore:

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()NON viene generato un errore, le asserzioni verranno colpite ma expect.assertions(2)fallirà e il test fallirà.


D'oh. Mi dimentico sempre della funzione di asserzioni multiple di Jest (probabilmente non la trovo personalmente la più intuitiva, ma sicuramente funziona in questi casi!) Saluti!
kpollock,

Questo ha funzionato perfettamente per me. Questo dovrebbe essere usato.
Ankit Tanna,

expect.hasAssertions()potrebbe essere un'alternativa migliore quando il test non ha asserzioni all'esterno catch, perché non è necessario aggiornare il numero se si aggiungono / rimuovono asserzioni.
André Sassi

12

Non l'ho provato da solo, ma suggerirei di usare l' asserzione di Jest per lanciare . Quindi immagino che il tuo esempio sarebbe simile a questo:

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

Ancora una volta, non l'ho provato ma penso che dovrebbe funzionare.


8

Jest ha un metodo toThrow(error)per testare che una funzione genera quando viene chiamata.

Quindi, nel tuo caso, dovresti chiamarlo così:

expect(t).toThrowError(TypeError);

I documenti


1
Non funzionerebbe per il caso: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });se il metodo deriso createnon lo è async.
Sergey

7

Il jest moderno ti consente di effettuare più controlli sul valore rifiutato. Per esempio:

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

fallirà con errore

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

- Expected
+ Received

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

6

La documentazione è chiara su come eseguire questa operazione. Diciamo che ho una funzione che accetta due parametri e genererà un errore se uno di essi lo è 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

Il tuo test

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

Nel caso in cui lavori con Promises:

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

Questo è super utile, grazie per risparmiare tempo !!
Aditya Kresna Permana,

3

Ho finito per scrivere un metodo pratico per la nostra libreria 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 stesso può essere fatto usando le funzionalità di Jests. Vedi la mia risposta per come è possibile farlo - stackoverflow.com/a/58103698/3361387
Kenny Body

3

Oltre al post di Peter Danis volevo solo enfatizzare la parte della sua soluzione che coinvolge "[passare] una funzione in attesa (funzione) .toThrow (vuoto o tipo di errore)".

In Jest, quando si verifica un caso in cui dovrebbe essere generato un errore, all'interno del wrapping di attesa () della funzione in fase di test, è necessario fornire un ulteriore livello di spostamento della funzione freccia affinché funzioni. ie

Sbagliato (ma approccio logico della maggior parte delle persone):

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

Destra:

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

È molto strano ma dovrebbe eseguire correttamente i test.


1

provare
expect(t).rejects.toThrow()


4
Perché try? non c'è tentativo, ma risposta. Se questa è la risposta, si prega di elaborare di più. cosa aggiungi alla risposta esistente?
dinder

7
Penso che @Razim stesse dicendo che dovresti provare la soluzione, non usare un tentativo di cattura.
Tom,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.