So testen Sie den Typ der ausgelösten Ausnahme in Jest

159

Ich arbeite mit einem Code, bei dem ich den von der Funktion ausgelösten Ausnahmetyp testen muss (Ist es TypeError, ReferenceError usw.)?

Mein aktuelles Testframework ist AVA und ich kann es als zweite Argumentmethode testen t.throws, wie hier:

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

Ich fing an, meine Tests in Jest umzuschreiben und konnte nicht herausfinden, wie ich das einfach machen konnte. Ist es überhaupt möglich?

Tranotheron
quelle

Antworten:

221

In Jest müssen Sie eine Funktion an Expect (Funktion) .toThrow (Leerzeichen oder Fehlertyp) übergeben.

Beispiel:

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

Wenn Sie eine vorhandene Funktion testen müssen, um festzustellen, ob sie mit einer Reihe von Argumenten ausgelöst wird, müssen Sie sie in require () in eine anonyme Funktion einschließen.

Beispiel:

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

Ein bisschen komisch, aber funktioniert und imho ist gut lesbar:

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 fangen Sie Ihre Ausnahme, dann können Sie auf Ihrem erhöhten testen Error. Seltsam expect(true).toBe(false);ist erforderlich, um Ihren Test nicht zu bestehen, wenn nicht erwartet Errorwird, dass er geworfen wird. Andernfalls ist diese Linie niemals erreichbar ( Errorsollte vor ihnen angehoben werden).

BEARBEITEN: @Kenny Body schlägt eine bessere Lösung vor, die die Codequalität verbessert, wenn Sie verwenden 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");
  }
});

Weitere Erklärungen finden Sie in der Originalantwort: So testen Sie den Typ der ausgelösten Ausnahme in Jest

Bodolsog
quelle
17
Dies ist eine sehr ausführliche Methode zum Testen auf Ausnahmen, wenn Jest bereits die Methode "Expect.toThrow ()" hat, um nach Ausnahmen zu suchen
gomisha
4
Ja, aber es wird nur der Typ, nicht die Nachricht oder anderer Inhalt getestet, und die Frage betraf die Testnachricht, nicht den Typ.
Bodolsog
2
Hah. Wirklich wie dieser, da mein Code einen Wert des ausgelösten Fehlers testen muss, damit ich die Instanz brauche. Ich würde die fehlerhafte Erwartung wie expect('here').not.toBe('here');zum Spaß schreiben :-)
Valery
4
@Valery oder: expect('to be').not.toBe('to be')im Shakespeare-Stil.
Michiel van der Blonk
2
am meisten unterschätzte Antwort!
Edwin Ikechukwu Okonkwo
40

Ich benutze eine etwas prägnantere Version:

expect(() => {
  //code block that should throw error
}).toThrow(TypeError) //or .toThrow('expectedErrorMessage')
Tal Joffe
quelle
2
Kurz und präzise.
Flapjack
31

Aufgrund meiner (wenn auch begrenzten) Exposition gegenüber Jest habe ich festgestellt, dass dies expect().toThrow()geeignet ist, wenn Sie NUR einen Fehler eines bestimmten Typs testen möchten:

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

ODER es wird ein Fehler mit einer bestimmten Meldung ausgegeben:

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

Wenn Sie versuchen, beides zu tun, erhalten Sie ein falsches Positiv. Wenn Ihr Code beispielsweise ausgelöst wird RangeError('Something bad happened!'), besteht dieser Test Folgendes:

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

Die Antwort von bodolsog, die die Verwendung eines try / catch vorschlägt, ist nah, aber anstatt zu erwarten, dass true falsch ist, um sicherzustellen, dass die erwarteten Zusicherungen im catch getroffen werden, können Sie stattdessen expect.assertions(2)zu Beginn Ihres Tests 2die Anzahl der erwarteten Zusicherungen verwenden . Ich bin der Meinung, dass dies die Absicht des Tests genauer beschreibt.

Vollständiges Beispiel zum Testen des Typs UND der Meldung eines Fehlers:

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

Wenn functionUnderTest()KEIN Fehler ausgegeben wird, werden die Zusicherungen getroffen, aber die schlagen expect.assertions(2)fehl und der Test schlägt fehl.

Kenny Body
quelle
D'oh. Ich vergesse immer die erwartete Funktion für mehrfache Behauptungen von Jest (möglicherweise finde ich es persönlich nicht besonders intuitiv, aber es funktioniert definitiv in solchen Fällen!) Prost!
Kpollock
Das hat bei mir sehr gut funktioniert. Dies sollte verwendet werden.
Ankit Tanna
expect.hasAssertions()Möglicherweise eine bessere Alternative, wenn der Test keine Zusicherungen außerhalb hat catch, da Sie die Nummer nicht aktualisieren müssen, wenn Sie Zusicherungen hinzufügen / entfernen.
André Sassi
12

Ich habe es nicht selbst versucht, aber ich würde vorschlagen, Jests toThrow- Behauptung zu verwenden. Ich denke, Ihr Beispiel würde ungefähr so ​​aussehen:

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

Auch hier habe ich es nicht getestet, aber ich denke, es sollte funktionieren.

Andrei CACIO
quelle
8

Jest hat eine Methode toThrow(error), um zu testen, ob eine Funktion beim Aufruf ausgelöst wird.

In Ihrem Fall sollten Sie es also so nennen:

expect(t).toThrowError(TypeError);

Die Dokumente

alexmac
quelle
1
Für den Fall würde es nicht funktionieren: jest.spyOn(service, 'create').mockImplementation(() => { throw new Error(); });Wenn die verspottete Methode createnicht ist async.
Sergey
7

Mit dem modernen Scherz können Sie den abgelehnten Wert genauer überprüfen. Beispielsweise:

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

wird mit Fehler fehlschlagen

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

- Expected
+ Received

  Object {
-   "statusCode": 500,
+   "statusCode": 404,
  }
Slava Baginov
quelle
6

In der Dokumentation ist klar, wie das geht. Nehmen wir an, ich habe eine Funktion, die zwei Parameter akzeptiert und einen Fehler auslöst, wenn einer davon ist 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

Dein 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()
  })
})
Gilbert
quelle
4

Wenn Sie mit Promises arbeiten:

await expect(Promise.reject(new HttpException('Error message', 402)))
  .rejects.toThrowError(HttpException);
Željko Šević
quelle
Das ist super hilfreich, danke, dass du mir Zeit gespart hast !!
Aditya Kresna Permana
3

Am Ende habe ich eine Convenience-Methode für unsere Test-Utils-Bibliothek geschrieben

/**
 *  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)
    }
  }
kpollock
quelle
Das gleiche kann mit Jests eigenen Funktionen gemacht werden. Siehe meine Antwort, wie dies getan werden kann - stackoverflow.com/a/58103698/3361387
Kenny Body
3

Neben Peter Danis 'Beitrag wollte er nur den Teil seiner Lösung hervorheben, der darin besteht, "eine Funktion in" Expect (Funktion) .toThrow (Leerzeichen oder Fehlertyp) zu übergeben ".

Wenn Sie in Jest auf einen Fall testen, in dem ein Fehler ausgelöst werden soll, müssen Sie innerhalb Ihrer erwarteten () Umhüllung der zu testenden Funktion eine zusätzliche Umbruchschicht für Pfeilfunktionen bereitstellen, damit sie funktioniert. Dh

Falsch (aber der logische Ansatz der meisten Leute):

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

Richtig:

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

Es ist sehr seltsam, sollte aber den Test erfolgreich ausführen.

Adrian
quelle
1

Versuchen
expect(t).rejects.toThrow()

Razim Saidov
quelle
4
Warum try? Es gibt keinen Versuch - sondern eine Antwort. Wenn dies eine Antwort ist, gehen Sie bitte näher darauf ein. Was fügen Sie der vorhandenen Antwort hinzu?
Dwinder
7
Ich denke, @Razim hat gesagt, dass Sie die Lösung ausprobieren sollten, nicht einen Try-Catch.
Tom