Jest explicitly or arbitrarily force fail() a test
When testing code with Jest, it can sometimes be useful to fail a test arbitrarily.
This post goes through a few scenarios where that might be useful and how to fail a Jest test explicitly/in a forced manner.
It also presents more idiomatic Jest patterns that could be used interchangeably.
Table of Contents
Fail() a synchronous test that should always throw with Jest
Here is our code under test:
function throwOrNot(shouldThrow = false) {
if (shouldThrow) {
throw new Error('shouldThrow was true');
}
return 'success';
}
Creating a naive test that only tests the “happy” path
Here is the naive test, which succeeds if the error is thrown.
it('should throw if passed true', () => {
try {
throwOrNot(true);
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/naive-throws-synchronous-passes.test.js
As we can see from the output, the test passes when put into the throw
branch of the test under code.
PASS src/naive-throws-synchronous-passes.test.js
✓ should throw if passed true (3ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.22s, estimated 2s
Ran all test suites matching /src\/naive-throws-synchronous-passes.test.js/i.
Imagine we modified throwOrNot
to stop satisfying this test (it doesn’t throw when passed true), the same test still passes.
function throwOrNot() {
return 'success';
}
it('should throw if passed true', () => {
try {
throwOrNot(true);
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/naive-throws-synchronous-false-positive.test.js
As per the following test run output, the tests are still passing despite the behaviour not being present any more:
PASS src/naive-throws-synchronous-false-positive.test.js
✓ should throw if passed true (1ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.762s
Ran all test suites matching /src\/naive-throws-synchronous-false-positive.test.js/i.
What we need to do is to make sure the try
block doesn’t continue executing if the throwOrNot
function executes without issue.
Force fail() a synchronous Jest test
function throwOrNot() {
return 'success';
}
it('should throw if passed true', () => {
try {
throwOrNot(true);
throw new Error("didn't throw");
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/fail-throws-synchronous.test.js
Output of the test run shows that if the code doens’t throw, the test suite will fail, which is desired behaviour:
FAIL src/fail-throws-synchronous.test.js
✕ should throw if passed true (9ms)
● should throw if passed true
expect(received).toEqual(expected) // deep equality
Expected: [Error: shouldThrow was true]
Received: [Error: didn't throw]
8 | throw new Error("didn't throw");
9 | } catch (error) {
> 10 | expect(error).toEqual(new Error('shouldThrow was true'));
| ^
11 | }
12 | });
13 |
at Object.toEqual (src/fail-throws-synchronous.test.js:10:19)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.883s
Ran all test suites matching /src\/fail-throws-synchronous.test.js/i.
Idiomatic Jest, fail() alternative: check a function throws using the .toThrow
Jest matcher
function throwOrNot() {
return 'success';
}
it('should throw if passed true', () => {
expect(throwOrNot.bind(null, true)).toThrow(
new Error('shouldThrow was true')
);
});
To run this example, see Running the examples to get set up, then run:
yarn test src/fail-throws-synchronous-to-throw.test.js
As in the previous example, the test fails since the code under test doesn’t throw, but this time we get a Received function did not throw
error, which is maybe more descriptive and shows the advantage of using the Jest .toThrow
matcher.
FAIL src/fail-throws-synchronous-to-throw.test.js
✕ should throw if passed true (5ms)
● should throw if passed true
expect(received).toThrow(expected)
Expected message: "shouldThrow was true"
Received function did not throw
4 |
5 | it('should throw if passed true', () => {
> 6 | expect(throwOrNot.bind(null, true)).toThrow(
| ^
7 | new Error('shouldThrow was true')
8 | );
9 | });
at Object.toThrow (src/fail-throws-synchronous-to-throw.test.js:6:39)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.795s
Ran all test suites matching /src\/fail-throws-synchronous-to-throw.test.js/i.
Fail() an async/await Jest test that should always throw with Jest
Creating a naive test that only tests the “happy” path
We define an async function for which we want to throw
under some condition (here if passed true
when called).
async function asyncThrowOrNot(shouldThrow = false) {
if (shouldThrow) {
throw new Error('shouldThrow was true');
}
return 'success';
}
The following test does actually test that the code under test behaves as expected (when it does work as expected).
it('should throw if passed true', async () => {
try {
await asyncThrowOrNot(true);
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/naive-throws-asynchronous-passes.test.js
The output of the test works with a correct implementation:
PASS src/naive-throws-asynchronous-passes.test.js
✓ should throw if passed true (4ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.724s, estimated 2s
Ran all test suites matching /src\/naive-throws-asynchronous-passes.test.js/i.
Imagine we modified asyncThrowOrNot
to stop satisfying this test (it doesn’t throw when passed true), the same test still passes.
async function asyncThrowOrNot() {
return 'success';
}
it('should throw if passed true', async () => {
try {
await asyncThrowOrNot(true);
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/naive-throws-asynchronous-false-positive.test.js
Tests are still passing, despite the code not doing what it’s supposed to (throwing), this is a false positive:
PASS src/naive-throws-asynchronous-false-positive.test.js
✓ should throw if passed true (2ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.341s, estimated 2s
Ran all test suites matching /src\/naive-throws-asynchronous-false-positive.test.js/i.
As in the previous section, we need to do is to make sure the try
block doesn’t continue executing if the asyncThrowOrNot
function executes without issue.
Force fail() an asynchronous Jest test
One way to arbitrarily fail a Jest test is to throw
an Error
in a branch or line of code that shouldn’t be reached:
async function asyncThrowOrNot() {
return 'success';
}
it('should throw if passed true', async () => {
try {
await asyncThrowOrNot(true);
throw new Error("didn't throw");
} catch (error) {
expect(error).toEqual(new Error('shouldThrow was true'));
}
});
To run this example, see Running the examples to get set up, then run:
yarn test src/fail-throws-asynchronous.test.js
Output shows the test isn’t passing any more (as is expected) but the error message is a bit cryptic Expected: [Error: shouldThrow was true] Received: [Error: didn't throw]
.
didn't throw
happens to be the message we added after await
-ing the function under test (see throw new Error("didn't throw");
).
FAIL src/fail-throws-asynchronous.test.js
✕ should throw if passed true (7ms)
● should throw if passed true
expect(received).toEqual(expected) // deep equality
Expected: [Error: shouldThrow was true]
Received: [Error: didn't throw]
8 | throw new Error("didn't throw");
9 | } catch (error) {
> 10 | expect(error).toEqual(new Error('shouldThrow was true'));
| ^
11 | }
12 | });
13 |
at Object.toEqual (src/fail-throws-asynchronous.test.js:10:19)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.203s, estimated 2s
Ran all test suites matching /src\/fail-throws-asynchronous.test.js/i.
Idiomatic Jest, fail() alternative: check an async function throws using expect().rejects.toEqual
The more idiomatic way to check an async
function throws is to use the await
or return
an expect(fn(param1)).rejects.toEqual(error)
.
Note: make sure to
await
orreturn
theexpect()
expression, otherwise Jest might not see the error as a failure but anUnHandledPromiseRejection
async function asyncThrowOrNot() {
return 'success';
}
it('should throw if passed true return expect()', async () => {
return expect(asyncThrowOrNot(true)).rejects.toEqual(
new Error('shouldThrow was true')
);
});
it('should throw if passed true await expect()', async () => {
await expect(asyncThrowOrNot(true)).rejects.toEqual(
new Error('shouldThrow was true')
);
});
To run this example, see Running the examples to get set up, then run:
yarn test src/fail-throws-asynchronous-rejects-to-equal.test.js
FAIL src/fail-throws-asynchronous-rejects-to-equal.test.js
✕ should throw if passed true return expect() (5ms)
✕ should throw if passed true await expect() (1ms)
● should throw if passed true return expect()
expect(received).rejects.toEqual()
Received promise resolved instead of rejected
Resolved to value: "success"
4 |
5 | it('should throw if passed true return expect()', async () => {
> 6 | return expect(asyncThrowOrNot(true)).rejects.toEqual(
| ^
7 | new Error('shouldThrow was true')
8 | );
9 | });
at expect (node_modules/expect/build/index.js:138:15)
at Object.expect (src/fail-throws-asynchronous-rejects-to-equal.test.js:6:10)
● should throw if passed true await expect()
expect(received).rejects.toEqual()
Received promise resolved instead of rejected
Resolved to value: "success"
10 |
11 | it('should throw if passed true await expect()', async () => {
> 12 | await expect(asyncThrowOrNot(true)).rejects.toEqual(
| ^
13 | new Error('shouldThrow was true')
14 | );
15 | });
at expect (node_modules/expect/build/index.js:138:15)
at Object.expect (src/fail-throws-asynchronous-rejects-to-equal.test.js:12:9)
Test Suites: 1 failed, 1 total
Tests: 2 failed, 2 total
Snapshots: 0 total
Time: 1.646s, estimated 2s
Ran all test suites matching /src\/fail-throws-asynchronous-rejects-to-equal.test.js/i.
Fail() a synchronous Jest test that shouldn’t throw
By default a synchronous Jest test that shouldn’t throw will fail if it throws:
it('should not throw', () => {
throw new Error('it threw');
});
To run this example, see Running the examples to get set up, then run:
yarn test src/synchronous-throw-fail.test.js
The following output shows how the test fails when the test throws.
FAIL src/synchronous-throw-fail.test.js
✕ should not throw (2ms)
● should not throw
it threw
1 | it('should not throw', () => {
> 2 | throw new Error('it threw');
| ^
3 | });
4 |
at Object.<anonymous> (src/synchronous-throw-fail.test.js:2:9)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.172s, estimated 2s
Ran all test suites matching /src\/synchronous-throw-fail.test.js/i.
Fail() an async/await Jest test that shouldn’t throw
By default an asynchronous (async/await) Jest test that shouldn’t throw will fail if it throws/rejects:
it('should not top-level throw', async () => {
throw new Error('it threw');
});
it('should not throw on Promise rejection', async () => {
await Promise.reject(new Error('Promise rejection'));
});
it('should not throw on async function throw', async () => {
const throws = async () => {
throw new Error('async-function throw');
};
await throws();
});
To run this example, see Running the examples to get set up, then run:
yarn test src/asynchronous-throw-fail.test.js
The following output shows how the test fails when the test throws.
Note how throw
in an it
callback async
function, await
-ing a Promise rejection and throw
in an await
-ed async function all fail the test.
FAIL src/asynchronous-throw-fail.test.js
✕ should not top-level throw (3ms)
✕ should not throw on Promise rejection (1ms)
✕ should not throw on async function throw
● should not top-level throw
it threw
1 | it('should not top-level throw', async () => {
> 2 | throw new Error('it threw');
| ^
3 | });
4 | it('should not throw on Promise rejection', async () => {
5 | await Promise.reject(new Error('Promise rejection'));
at Object.<anonymous> (src/asynchronous-throw-fail.test.js:2:9)
● should not throw on Promise rejection
Promise rejection
3 | });
4 | it('should not throw on Promise rejection', async () => {
> 5 | await Promise.reject(new Error('Promise rejection'));
| ^
6 | });
7 | it('should not throw on async function throw', async () => {
8 | const throws = async () => {
at Object.<anonymous> (src/asynchronous-throw-fail.test.js:5:24)
● should not throw on async function throw
async-function throw
7 | it('should not throw on async function throw', async () => {
8 | const throws = async () => {
> 9 | throw new Error('async-function throw');
| ^
10 | };
11 |
12 | await throws();
at throws (src/asynchronous-throw-fail.test.js:9:11)
at Object.throws (src/asynchronous-throw-fail.test.js:12:9)
Test Suites: 1 failed, 1 total
Tests: 3 failed, 3 total
Snapshots: 0 total
Time: 1.304s
Ran all test suites matching /src\/asynchronous-throw-fail.test.js/i.
Jest is Promise-aware, so throw
, rejection is all the same.
Running the examples
Clone github.com/HugoDF/jest-force-fail.
Run yarn install
or npm install
(if you’re using npm
replace instance of yarn
with npm run
in commands).
Conclusion
The example show you how to use throw new Error('testingError')
to force fail()
a Jest (and other test library) test.
This works in synchronous and asynchronous (async/await) Jest tests. In the asynchronous case, it’s because Jest is Promise-aware.
In Jest/JavaScript, a fail
functions could be defined as follows (just throws an Error
):
function fail() {
throw new Error('Test was force-failed');
}
The idiomatic way to do this in Jest however is to use expect().toThrow()
in the synchronous case:
expect(fn.bind(null, param1, param2)).toThrow(new Error('specify the error'));
And return/await expect().rejects.toEqual()
in the asynchronous (async/await) case:
return expect(asyncFn(param1, params)).rejects.toThrow(new Error('specific the error'));
// or
await expect(asyncFn(param1, params)).rejects.toThrow(new Error('specific the error'));
Further Reading
About async functions and the internals of that, I’ve written a longer post: Async JavaScript: history, patterns and gotchas
Get The Jest Handbook (100 pages)
Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.
orJoin 1000s of developers learning about Enterprise-grade Node.js & JavaScript