Testing exceptions in Jest (Sync and Async code)

avatar
Borislav Hadzhiev

Last updated: Mar 7, 2024
8 min

banner

# Table of Contents

  1. How to test exceptions in Jest
  2. How to test an exception in Jest using try/catch
  3. Testing Async functions that throw exceptions using Jest
  4. Testing Async functions that throw exceptions using try/catch

Note: if you need to test ASYNC code that throws, click on the following subheading:

# How to test exceptions in Jest

To test an exception in Jest:

  1. Pass a function that throws an exception to the expect() function.
  2. Use the toThrow() method to test the type of the thrown error.
  3. Use the toThrow() method to test the error message.
index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = () => { throw new TypeError('A TypeError occurred'); }; expect(throwTypeError).toThrow(TypeError); expect(throwTypeError).toThrow('A TypeError occurred'); });

test exception in jest

The throwTypeError function simply throws a TypeError exception.

We passed the function directly to the expect() function.

The first call to expect() tests the type of the thrown exception.

index.test.js
expect(throwTypeError).toThrow(TypeError);

The second call tests if the message of the thrown error contains a certain string.

index.js
expect(throwTypeError).toThrow('A TypeError occurred');

You can call the toThrow() method without passing it any arguments to test if the function throws an error.

index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = () => { throw new TypeError('A TypeError occurred'); }; expect(throwTypeError).toThrow(TypeError); expect(throwTypeError).toThrow('A TypeError occurred'); expect(throwTypeError).toThrow(); });

If the function you're calling takes arguments, pass an inline arrow function to the expect() method to supply the necessary arguments.

index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = message => { throw new TypeError(message); }; const errorMessage = 'A TypeError occurred'; expect(() => { throwTypeError(errorMessage); }).toThrow(TypeError); expect(() => { throwTypeError(errorMessage); }).toThrow(errorMessage); expect(() => { throwTypeError(errorMessage); }).toThrow(); });

The throwTypeError() function now takes an argument.

We have to call the expect() function with a reference to the function that throws and not with an error.

We passed an inline arrow function to expect() to provide the required argument and used the toThrow() method like in the previous examples.

If you call the expect() function with the output of calling the throwTypeError function, e.g. expect(throwTypeError('test')), then the throwTypeError function would immediately throw and fail your test.

Instead, we have to pass a reference to a function to the expect() function.

The optional argument you can pass to the toThrow() method can be:

  • a regular expression that matches the thrown error message
  • a string that is contained in the thrown error message
  • an error class that is subclassed by the thrown error message
  • an error object whose message property is equal to the message property of the thrown error

Here are some examples.

index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = message => { throw new TypeError(message); }; const throwTypeErrorWithMessage = () => { throwTypeError('A TypeError occurred'); }; expect(throwTypeErrorWithMessage).toThrow(/a typeerror/i); expect(throwTypeErrorWithMessage).toThrow('A TypeError'); expect(throwTypeErrorWithMessage).toThrow( /^A TypeError occurred$/i, ); expect(throwTypeErrorWithMessage).toThrow( new TypeError('A TypeError occurred'), ); expect(throwTypeErrorWithMessage).toThrow(TypeError); });

test thrown exception in multiple ways

The first example uses a regular expression to check if a certain string is contained in the error message in a case-insensitive manner.

index.test.js
expect(throwTypeErrorWithMessage).toThrow(/a typeerror/i);

The forward slashes / / mark the beginning and end of the regular expression.

The i flag allows us to perform a case-insensitive search in the string.

You can remove the i if you only want to perform a case-sensitive search.

index.test.js
expect(throwTypeErrorWithMessage).toThrow(/a typeerror/);

The second example tests whether a certain substring is contained in the error message.

index.test.js
expect(throwTypeErrorWithMessage).toThrow('A TypeError');

The next example uses a regular expression to check if the error message is equal to a specific string.

index.test.js
expect(throwTypeErrorWithMessage).toThrow( /^A TypeError occurred$/i, );

The caret ^ matches the beginning of the input and the dollar sign $ matches the end of the input.

The string in the example has to be equal to the error message.

Remove the i flag if you want to perform a case-sensitive search.

The next example checks if the supplied error matches the thrown from the function error.

index.test.js
expect(throwTypeErrorWithMessage).toThrow( new TypeError('A TypeError occurred'), );

The last example tests if the thrown error is of a specific type.

index.test.ts
expect(throwTypeErrorWithMessage).toThrow(TypeError);

# Table of Contents

  1. How to test an exception in Jest using try/catch
  2. Testing Async functions that throw exceptions using Jest
  3. Testing Async functions that throw exceptions using try/catch

# How to test an exception in Jest using try/catch

You can also use a try/catch statement to test an exception in Jest.

index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = message => { throw new TypeError(message); }; // 👇️ specify how many assertions the test contains expect.assertions(3); try { throwTypeError('An error occurred'); } catch (err) { expect(err.message).toBe('An error occurred'); expect(err.message).toMatch(/an error/i); expect(err instanceof TypeError).toBe(true); } });

The except.assertions() method call enables you to specify how many assertions there are in the test.

If the actual number of assertions doesn't match the expected number, the test fails.

This is useful when you want to ensure that all your assertions are run.

We call the throwTypeError function in the try block and the error is passed to the catch() function.

The catch() function is where we test the caught error.

If the function you're testing doesn't throw an error, the catch() function would never run.

As expected, the test would still fail because you called the expect.assertions() method with the expected number of assertions.

You can also use the toBeInstanceOf() and toHaveProperty helper methods in a similar way.

index.test.js
it('tests the type of a thrown error', () => { const throwTypeError = message => { throw new TypeError(message); }; // 👇️ specify how many assertions the test contains expect.assertions(2); try { throwTypeError('An error occurred'); } catch (err) { expect(err).toBeInstanceOf(TypeError); expect(err).toHaveProperty('message', 'An error occurred'); } });

The assertions test whether the caught error is an instance of TypeError and has a message property that is equal to the specified string.

# Testing Async functions that throw exceptions using Jest

To test async functions that throw exceptions in Jest:

  1. Await the call to the expect() function.
  2. Use the rejects.toThrow() method to test the type of the thrown error and the error message.
index.test.ts
it('tests the type of a thrown error', async () => { const throwTypeError = message => { return Promise.reject(new TypeError(message)); }; await expect(() => throwTypeError('A TypeError occurred'), ).rejects.toThrow(TypeError); await expect(() => throwTypeError('A TypeError occurred'), ).rejects.toThrow('A TypeError occurred'); });

test async function that throws

Notice that we marked the callback function that we passed to it() as async.

This way, we are able to use the await keyword to await the call to the expect() function.

The .rejects property is used to unwrap the reason of a rejected promise, so that another matcher can be chained.

The first assertion tests whether the function rejected a promise with a TypeError.

index.test.js
await expect(() => throwTypeError('A TypeError occurred'), ).rejects.toThrow(TypeError);

The second assertion tests whether a given substring is contained in the error message.

index.test.js
await expect(() => throwTypeError('A TypeError occurred'), ).rejects.toThrow('A TypeError occurred');

Make sure to use the await keyword to await the result of calling expect().

Something that commonly causes issues is trying to await the async function you pass to expect() by mistake.

The optional argument you can pass to the toThrow() method can be:

  • a regular expression that matches the thrown error message
  • a string that is contained in the thrown error message
  • an error class that is subclassed by the thrown error message
  • an error object whose message property is equal to the message property of the thrown error

Here are some examples.

index.test.js
it('tests the type of a thrown error', async () => { const throwTypeError = message => { return Promise.reject(new TypeError(message)); }; const throwTypeErrorWithMessage = () => { return throwTypeError('A TypeError occurred'); }; await expect(throwTypeErrorWithMessage).rejects.toThrow( /a typeerror/i, ); await expect(throwTypeErrorWithMessage).rejects.toThrow( 'A TypeError', ); await expect(throwTypeErrorWithMessage).rejects.toThrow( /^A TypeError occurred$/i, ); await expect(throwTypeErrorWithMessage).rejects.toThrow( new TypeError('A TypeError occurred'), ); await expect(throwTypeErrorWithMessage).rejects.toThrow( TypeError, ); });

The first example tests whether a regular expression is matched in the error message.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( /a typeerror/i, );

The forward slashes / / mark the beginning and end of the regular expression.

The i flag allows us to perform a case-insensitive search in the string.

You can remove the i if you only want to perform a case-sensitive search.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( /a typeerror/, );

The second example tests whether a certain substring is contained in the error message.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( 'A TypeError', );

The next example uses a regular expression to check if the error message is equal to a specific string.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( /^A TypeError occurred$/i, );

The caret ^ matches the beginning of the input and the dollar sign $ matches the end of the input.

The string in the example has to be equal to the error message.

Remove the i flag if you want to perform a case-sensitive search.

The next example checks if the supplied error matches the thrown from the function error.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( new TypeError('A TypeError occurred'), );

The last example tests if the thrown error is of a specific type.

index.test.js
await expect(throwTypeErrorWithMessage).rejects.toThrow( TypeError, );

# Testing Async functions that throw exceptions using try/catch

You can also use a try/catch statement to test async functions that throw errors in Jest:

  1. Wrap your call to the function that throws in a try block.
  2. Test the type of the error and the error message in the catch block.
index.test.js
it('tests the type of a thrown error', async () => { const throwTypeError = message => { return Promise.reject(new TypeError(message)); }; expect.assertions(3); try { await throwTypeError('A TypeError occurred'); } catch (err) { expect(err.message).toBe('A TypeError occurred'); expect(err.message).toMatch(/a typeerror/i); expect(err instanceof TypeError).toBe(true); } });

Notice that we used the expect.assertions() method to specify how many assertions the test contains.

If the expected number of assertions doesn't match the actual number, the test fails.

In our try block, we simply call the async function that throws and await the Promise.

The error that is thrown from the function gets passed to the catch() function.

The catch() function tests whether the error message matches the expectations and whether the thrown error is an instance of TypeError.

If the function doesn't throw an error, the catch() function is never called and the test fails because the expected number of assertions (3) is not equal to the actual number of assertions (0).

You can also use the toBeInstanceOf and toHaveProperty helper methods.

index.test.js
it('tests the type of a thrown error', async () => { const throwTypeError = message => { return Promise.reject(new TypeError(message)); }; expect.assertions(2); try { await throwTypeError('A TypeError occurred'); } catch (err) { expect(err).toBeInstanceOf(TypeError); expect(err).toHaveProperty( 'message', 'A TypeError occurred', ); } });

The first assertion tests whether the thrown error is an instance of TypeError.

index.test.js
expect(err).toBeInstanceOf(TypeError);

The second example tests whether the error has a message property that is equal to a given string.

index.test.js
expect(err).toHaveProperty( 'message', 'A TypeError occurred', );

Which approach you pick is a matter of personal preference. I'd use the await expect() syntax from the previous subheading as I find it more concise and just as readable.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.