How to test the type of a thrown exception in Jest

I'm working with some code where I need to test the type of an exception thrown by a function (is it TypeError, ReferenceError, etc.?).

My current testing framework is AVA and I can test it as a second argument t.throws method, like here:

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

I started rewriting my tests in Jest and couldn't find how to easily do that. Is it even possible?


Solution 1:

In Jest you have to pass a function into expect(function).toThrow(<blank or type of error>).

Example:

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

Or if you also want to check for error message:

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

If you need to test an existing function whether it throws with a set of arguments, you have to wrap it inside an anonymous function in expect().

Example:

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

Solution 2:

It is a little bit weird, but it works and IMHO is good readable:

it('should throw Error with message \'UNKNOWN ERROR\' when no parameters 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");
  }
});

The Catch block catches your exception, and then you can test on your raised Error. Strange expect(true).toBe(false); is needed to fail your test if the expected Error will be not thrown. Otherwise, this line is never reachable (Error should be raised before them).

@Kenny Body suggested a better solution which improve a code quality if you use expect.assertions():

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

See the original answer with more explanations: How to test the type of a thrown exception in Jest

Solution 3:

I use a slightly more concise version:

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

Solution 4:

From my (albeit limited) exposure to Jest, I have found that expect().toThrow() is suitable if you want to only test an error is thrown of a specific type:

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

Or an error is thrown with a specific message:

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

If you try to do both, you will get a false positive. For example, if your code throws RangeError('Something bad happened!'), this test will pass:

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

The answer by bodolsog which suggests using a try/catch is close, but rather than expecting true to be false to ensure the expect assertions in the catch are hit, you can instead use expect.assertions(2) at the start of your test where 2 is the number of expected assertions. I feel this more accurately describes the intention of the test.

A full example of testing the type and message of an 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!');
        }
    });
});

If functionUnderTest() does not throw an error, the assertions will be be hit, but the expect.assertions(2) will fail and the test will fail.