jest: Unhandled Promise Rejection Logging

Do you want to request a feature or report a bug? feature

What is the current behavior? When writing unit tests that involve promises, if a developer does not return the promise (or use async/await), uncaught rejections bubble up to Node’s default unhandledRejection handler. For example:

image

That’s fine, except that when running tests locally, because of the way Jest aggressively clears the console as concurrent test suites finish running, Node’s default unhandledRejection logging sometimes gets cleared off the terminal. I’ve found this to be especially problematic on machines with more CPUs where many tests may be running concurrently.

That can make spotting these errors trickier in development. The default node error handler always prints the message in CI, when Jest isn’t clearing the console at all, but in dev it’s inconsistent.

If the current behavior is a bug, please provide the steps to reproduce and either a repl.it demo through https://repl.it/languages/jest or a minimal repository on GitHub that we can yarn install and yarn test.

What is the expected behavior? It would be great if Jest had a default unhandledRejection handler baked in that provided the same error message and stack trace formatting we get for other errors. And, more importantly, if the default handler could ensure the error would not get wiped away by any console clearing Jest does in dev.

As a quick fix, I added an unhandledRejection handler in my Jest setup that logs the error to the console.

image

But if you guys think this could be useful to other people, I’d be happy to take a stab at implementing a more robust solution in Jest itself!

Please provide your exact Jest configuration and mention your Jest, node, yarn/npm version and operating system. Jest v19.0.2 Node v6.9.4 yarn v0.21.3 Mac OS X 10.12.3

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 35
  • Comments: 24 (12 by maintainers)

Commits related to this issue

Most upvoted comments

@FredyC here’s how I avoid the memory leak:

// In Node v7 unhandled promise rejections will terminate the process
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
  process.on('unhandledRejection', reason => {
    throw reason
  })
  // Avoid memory leak by adding too many listeners
  process.env.LISTENING_TO_UNHANDLED_REJECTION = true
}

In this snippet I don’t just log the error but I throw it to make sure jest test will return a non-zero exit code. This is very useful in a ci context.

A small workaround I’ve figured out. Use setupFiles configuration option and create a simple file like this. If you need it for a browser, just check out this..

process.on('unhandledRejection', (reason) => {
	console.log('REJECTION', reason)
})

Then suddenly all warnings are replaced by nice stack trace 😃

Edit: One caveat is this warning instead. I am not sure what happens with a really large project. (node:8452) Warning: Possible EventEmitter memory leak detected. 11 unhandledRejection listeners added. Use emitter.setMaxListeners() to increase limit

This seems to have been implemented in Jest 21, and it is extremely annoying. Any asynchronously handled promises that is rejected before having a handler attached makes tests fail. And I don’t find any way to deactive this behaviour.

Any idea of how to overcome this? Thanks.

i think the easiest way to implement something like this is to have a global handler and save all unhandled errors to a test state. then when all tests are done, check the state, if there’s any errors, fail the test and log them

Yeah this is kind of a blocker for me. Here’s a small test to demonstrate the issue:

test('this should work', (done) => {
    const n = Promise.reject('nope');

    setTimeout(() => {
        n.catch((err) => console.log('Caught: ', err));
    });

    setTimeout(() => {
        // Never makes it to this point
        console.log('here');
        done();
    }, 10);
});

This test fails in Jest 21.x, but runs fine in Jest 20.x. If I were to catch the exception synchronously (NOT inside a setTimeout), the test passes in Jest 21.x as expected.

This looks like a contrived exception, but in my actual code, I’m having a Promise reject inside of an Express router that I’m testing. I’m testing the router using Supertest, so I have to use done() after running assertions on the http response.

yea! I remember we already thought about it in the past, but couldn’t implement it because of Jasmine dependency. Now that we started migrating away from jasmine i think it’d be pretty easy to implement!

@stipsan That’s kinda surprising that setting env variable here actually makes it shared between other jest child processes. Not sure how that works really, but good to know 😃

This seems to have just gotten a lot more important with Jest 20. I’m seeing expectations that are after a.resolves like .resolves.isInstanceOf() just print this UnhandledPromiseRejectionWarning and don’t even register with the jest final output that there was a test failure.

@ramusus Unfortunately, the best I could do was just downgrade to 20.x. I haven’t looked into updating back to 21.x since Oct though, so maybe things have changed.