The following test fails because a rejected Promise is set as return value:
test("My test", async () => {
const myMockFunc = jest.fn().mockReturnValueOnce(Promise.reject("error"))
setTimeout(() => myMockFunc(), 100)
await new Promise(resolve => setTimeout(resolve, 200))
expect(myMockFunc.mock.calls.length).toBe(1)
})
The test should pass. If e.g. a resolved Promise is returned it passes.
Execute the test above.
npx envinfo --preset jestSystem:
OS: macOS 10.14.3
CPU: (8) x64 Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz
Binaries:
Node: 11.12.0 - /usr/local/bin/node
npm: 6.7.0 - /usr/local/bin/npm
npmPackages:
jest: ^24.5.0 => 24.5.0
Because using Promise.reject() will create a rejected promise immediately, a rejected promise without catch will throw an error, so the test fails.
You can return rejected promise like below.
It create a rejected promise when you call this function, instead of in the declaration.
jest.fn(() => Promise.reject('error'));
//or
jest.fn().mockImplementationOnce(() => Promise.reject('error'));
The test will look like:
test('My test', async () => {
const myMockFunc = jest.fn(() => Promise.reject('error'));
setTimeout(
() =>
myMockFunc().catch(e => {
console.log(e); // error
}),
100
);
await new Promise(resolve => setTimeout(resolve, 200));
expect(myMockFunc.mock.calls.length).toBe(1);
});
This is intended behavior, if an unhandled rejection occurs while your tests are running, they will fail. You should handle rejections as @WeiAnAn described.
The minimal repro for this is something like
test("t", async () => {
Promise.reject();
await new Promise(resolve => setTimeout(resolve, 0));
});
Thank you for your clarification. That cleared things up for me a lot. Thank you for this amazing test framework!
This is intended behavior, if an unhandled rejection occurs while your tests are running, they will fail.
How can the test runner know that the promise is unhandled before the test has finished running? It is perfectly valid to handle a rejected promise _after_ is has rejected, in which case it is no longer unhandled, and Jest should let you do that.
test("t", (done) => {
var p = Promise.reject();
setTimeout(() => {
p.catch(done)
}, 0);
});
The workaround of course is to catch your promise and turn it into into some sort of result type, then unwrap it again just before you attach your catch handler, but this seems like unnecessary gymnastics to go through just to appease the test framework.
@juggernaut0 No, it is not perfectly valid. Running node on
const p = Promise.reject();
setTimeout(() => {
p.catch(() => {});
}, 0);
results in
(node:23000) UnhandledPromiseRejectionWarning: undefined
(node:23000) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:23000) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:23000) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
Code that causes Node warnings / relies on deprecated Node behavior rightly causes tests to fail.