The two ways to test errors in Jest
Method one
The simplest way to test for errors being thrown in Jest is using one of the three built-in matchers:
toThrow(error?)
whereerror
is a matcher for the specific type or message of the thrown error. To match the message you have three options: Regular expression, exact string match or passing an error instance where the message will be directly compared. To match the type you can pass an Error class and it will check if the error was created from it (instanceof
).toThrowErrorMatchingSnapshot(hint?)
where hint is some string to uniquely identify the snapshot. I would not recommend using this, as error messages are usually minimal and moving them into a different snapshot file just disconnects the test from the expected outcome.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
whereinlineSnapshot
is the error message itself, this is the same astoThrow
with an exact string match with the added benefit of automatically being updated without having to copy/paste the message from the console.
Something to note when using any of these functions is that you should be passing a function to expect
, not a value like you usually would. This is because Jest needs to manage when your function is called so it can wrap it in a try
/catch
internally. In the case of asynchronous code you can just pass a Promise
and do .rejects.toThrow()
.
For most cases the above functions should suffice. However, you may have noticed they only look at either the message of the error or its Error class. What if you want to go deeper, for example there is a new Error class that has landed in several browsers and is in newer versions of Node.js called AggregateError. It groups multiple errors together by storing them as ‘children’ on a property called “errors”. We could test if we are getting an AggregateError
or if the message of said error is as expected, but not what each of the internal errors are. This brings us to our second way of testing for errors in Jest.
Method 2
Using try
/catch
in a Jest test function is the second method, this may seem obvious but what if no error is thrown, the test would still pass. Jest has us covered here with a function for checking how many assertions were made in a test: expect.hasAssertions()
, or even expect.assertions(number)
if you want to make sure a specific number of assertions were made. I have been using Jest for years and have only just discovered these two functions (hence the blogpost). So, let’s look at an example:
function function foo(): void
foo() {
throw new var AggregateError: AggregateErrorConstructor
new (errors: Iterable<any>, message?: string | undefined, options?: ErrorOptions | undefined) => AggregateError (+1 overload)
AggregateError([new var Error: ErrorConstructor
new (message?: string | undefined, options?: ErrorOptions | undefined) => Error (+1 overload)
Error("bar"), new var Error: ErrorConstructor
new (message?: string | undefined, options?: ErrorOptions | undefined) => Error (+1 overload)
Error("baz")], "foo");
}
var test: jest.It
(name: string, fn?: jest.ProvidesCallback | undefined, timeout?: number | undefined) => void
test("AggregateError has specific child errors", () => {
// We are telling Jest that this test definitely has assertions.
// Meaning if expect is not called at least once it will fail.
// So if foo does not throw an error our test will fail.
const expect: jest.Expect
expect.jest.Expect.hasAssertions(): void
hasAssertions();
try {
function foo(): void
foo();
} catch (function (local var) error: unknown
error) {
// We now have the full error object and can test whatever we like.
const expect: jest.Expect
<any>(actual: any) => jest.JestMatchers<any>
expect(function (local var) error: unknown
error.message).jest.Matchers<void, any>.toBe<string>(expected: string): void
toBe("foo");
const expect: jest.Expect
<any>(actual: any) => jest.JestMatchers<any>
expect(function (local var) error: unknown
error.errors).jest.Matchers<void, any>.toHaveLength(expected: number): void
toHaveLength(2);
const expect: jest.Expect
<any>(actual: any) => jest.JestMatchers<any>
expect(function (local var) error: unknown
error.errors[0]?.message).jest.Matchers<void, any>.toBe<string>(expected: string): void
toBe("bar");
const expect: jest.Expect
<any>(actual: any) => jest.JestMatchers<any>
expect(function (local var) error: unknown
error.errors[1]?.message).jest.Matchers<void, any>.toBe<string>(expected: string): void
toBe("baz");
}
});