jest: Unhandled async errors crash Jest.

On master, I changed one of the tests in jest-haste-map from toBeDefined() or something to not.toBeDefined(). It would then go on to crash Jest:

[cpojer: ~/Projects/jest (master)]$ jest "haste-map.*index"

 RUNS  packages/jest-haste-map/src/__tests__/index-test.js

/Users/cpojer/Projects/jest/packages/jest-jasmine2/node_modules/jest-matchers/build/index.js:110
        throw error;
        ^
Error: expect(received).toBeNull()

Expected value to be null, instead received
  undefined
    at HasteMap.hasteMap.once (/Users/cpojer/Projects/jest/packages/jest-haste-map/src/__tests__/index-test.js:685:62)
    at HasteMap.g (events.js:291:16)
    at emitOne (events.js:96:13)
    at HasteMap.emit (events.js:188:7)
    at Timeout.emitChange (/Users/cpojer/Projects/jest/packages/jest-haste-map/src/index.js:562:14)
    at ontimeout (timers.js:365:14)
    at tryOnTimeout (timers.js:237:5)
    at Timer.listOnTimeout (timers.js:207:5)

@dmitriiabramov what have we done?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 15
  • Comments: 46 (21 by maintainers)

Commits related to this issue

Most upvoted comments

That’s ok, we can disagree on this but the Jest project doesn’t need your negativity and we won’t accept this behavior here.

@cpojer sorry I am just very enthusiastic about my favorite OSS tools. I wasn’t aware that displaying negative emotions about negative things is prohibited in jest repo. Next time, I will try to write my comments like a robot, deprived of any emotions.

@capaj seems like you are on the hook to make that PR. Jest is an open source project, if you’d like something to be fixed, please start contributing. The attitude you are showing on this issue tracker is not appropriate.

@cpojer I beg to disagree, but that’s a discussion better suited for some other place/platform

I have started working on it and have a proof of concept. Now will need to clean it up and prepare so it can be made into a PR

screen shot 2017-07-12 at 14 21 26

Any chance to fix this bug? Why isn’t this basic node functionality supported?

Currently we use in our tests a utility to work around this in a quite cumbersome way:

/**
 * In async tests, JEST will die (in watch mode) if an exception is thrown from a callback. This utility will catch
 * the errors instead and report the test as failed in these case *
 *
 * @param {jest.DoneCallback} done
 * @param {T} callback
 * @returns {T}
 */
export function catchErrors<T extends Function>(done: jest.DoneCallback, callback: T): T {
	return function() {
		try {
			callback.apply(this, arguments)
		} catch (e) {
			done.fail(e)
		}
	} as any as T
}

which then can be used as follows:

describe("suite", () => {
	it("should handle async exceptions", (done) => {
                setTimeout(catchErrors(done, (err, res) => {
                        throw "Catched by catchErrors so that our tests properly fail!"
                        done() 
                }), 100)
        })
})

Any cleaner approach to this would be appreciated!

This is part of Jest 21 thanks to @dignifiedquire. Published jest@test (Jest 21 beta) so you can give this a try. Also enjoy a substantially faster Jest startup.

@mweststrate this should be in the jest core. Seriously, this has been lying here for two months and no one opened a PR yet? 😖

Oh, it might have been because I didn’t return the promise but instead am using Jasmine’s async work. Nevertheless, we should hook up an unhandled promise handler in Jest.

In mocha, the done callback takes an optional error (standard Node.js cb style).

If you call done with a truthy value then it will be used as an error.

promise.then(x => {
  // do something
  done(); // successful
}).catch(err => {
  // do something with error
  done(err); // hand error to test runner
});

Just another voice chiming in here - this (slash, #1873) is a surprisingly easy issue to run into and caused me to spend quite a bit of time trying to figure out why my tests were reporting a timeout failure rather than the actual root cause of the test failure.

Still happening in 22?

Looks like ^ fails when using jsdom, but not when using the node env.

Using node:

$ npx jest --env node
 FAIL  test/index.test.js
  ● exception promise

    expect(received).toEqual(expected)
    
    Expected value to equal:
      false
    Received:
      true
      
      at Timeout.setTimeout [as _onTimeout] (test/index.test.js:3:18)
      at ontimeout (timers.js:469:11)
      at tryOnTimeout (timers.js:304:5)
      at Timer.listOnTimeout (timers.js:264:5)

  ✕ exception promise (18ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.216s, estimated 6s
Ran all test suites.

Using jsdom:

$ npx jest --env jsdom
 FAIL  test/index.test.js (5.114s)
  ● exception promise

    Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
      
      at node_modules/jest-jasmine2/build/queue_runner.js:65:21
      at ontimeout (timers.js:469:11)
      at tryOnTimeout (timers.js:304:5)
      at Timer.listOnTimeout (timers.js:264:5)

  ✕ exception promise (5003ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        5.733s
Ran all test suites.

Minimal repro: https://github.com/kjbekkelund/jest-timeout-issue-jsdom

this is quite frustrating as I have no idea where it’s dying

At the very least, someone might want to update the documentation.

From https://facebook.github.io/jest/docs/en/asynchronous.html#callbacks:

test('the data is peanut butter', done => {
  function callback(data) {
    expect(data).toBe('peanut butter');
    done();
  }

  fetchData(callback);
});

(If that expectation fails, the test will still fail, but only because the done() call is never reached.)

I am still experiencing this in 22.1.1 in the following test:

it('replies with the correct message': done => {
  // Initialise a bot with the MS botbuilder framework
  bot.on('send', message => {
     expect(message.text).toEqual('Is everything ok?');
     done();
   });
});

Seems to be a gift that gives on giving…

@dignifiedquire @cpojer I still got timeout for the following test. I am sure I installed version 21.1.0 and I also tried version 21.0.2. Did I miss anything ?

test.spec.js

it('exception promise', (done) => {
        setTimeout(()=>{
                expect(true).toEqual(false)
                done()
        }, 10)
})

package.json

{
  "name": "testJest",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "jest": "21.1.0"
  },
  "scripts": {
    "test": "jest --debug"
  }
}

image

@dignifiedquire it doesn’t do it yet actually! and i think work that you’re doing in https://github.com/facebook/jest/pull/4016/files can be reused in both

This is some huge drawback for me 😦 Trying to find a way to circumvent this properly…