jest: "Modern" fake-timer implementation doesn't work with PromiseJS.
Awesome work on https://github.com/facebook/jest/pull/7776, thanks for that!!
š Bug Report
Iām using Jest 26.1.0.
Testing async code with Promises, as described in this Jest doc, doesnāt seem to work with jest.useFakeTimers('modern');
(from https://github.com/facebook/jest/pull/7776; documented here) if youāre using the āpromiseā library from NPM.
For a while (starting in https://github.com/facebook/react-native/commit/3ff39870ce776c653823e1733363be0401896294), React Native has used that library as part of their Jest setup (which you get if you use preset: 'react-native'
), so I expect this will affect most people using React Native (this is how I encountered it).
Otherwise, Iām not sure how often people use that āpromiseā library, but itās an official solution given in a Jest troubleshooting doc, and pops up as a workaround for some issues in this repo (e.g. here).
This is arguably a regression (or would be when āmodernā becomes the default), as this behavior isnāt observed with jest.useFakeTimers('legacy');
or jest.useRealTimers()
.
To Reproduce
E.g.,
global.Promise = require('promise');
jest.useFakeTimers('modern');
test('1 equals 1', async () => {
await Promise.resolve();
expect(1).toEqual(1);
});
Expected behavior
I expect the test to pass. Instead, five seconds go by and I get this failure:
ā 1 equals 1 (5006 ms)
ā 1 equals 1
: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
3 | jest.useFakeTimers('modern');
4 |
> 5 | test('1 equals 1', async () => {
| ^
6 | await Promise.resolve();
7 | expect(1).toEqual(1);
8 | });
at new Spec (node_modules/jest-jasmine2/build/jasmine/Spec.js:116:22)
at Object.<anonymous> (fetchData.test.js:5:1)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 5.762 s, estimated 6 s
Ran all test suites.
error Command failed with exit code 1.
Link to repl or repo (highly encouraged)
In the repro linked below, Iāve closely imitated all the tests from the sections in the āTesting Asynchronous Codeā doc that use Promises, to suggest that the failure happens in usual, well-documented ways of testing (provided youāre using PromiseJS).
In its current state, you should observe these timeout errors on tests that use Promises (just run yarn
and yarn test
).
Try uncommenting global.Promise = require('promise');
at the top of fetchData.test.js
; the tests should all pass.
Try doing something other than jest.useFakeTimers('modern');
; the tests should all pass.
(Incidentally, note that accidentally calling jest.useFakeTimers
multiple times with different arguments in the same file seems disruptive and might hamper your investigation. Also, be sure to comment out jest.runAllTimers()
if youāre using real timers.)
https://github.com/chrisbobbe/jest-async-modern-timers-repro
envinfo
System:
OS: macOS 10.15.5
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Binaries:
Node: 10.20.1 - ~/.nvm/versions/node/v10.20.1/bin/node
Yarn: 1.22.4 - /usr/local/bin/yarn
npm: 6.14.4 - ~/.nvm/versions/node/v10.20.1/bin/npm
npmPackages:
jest: ^26.1.0 => 26.1.0
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 31
- Comments: 32 (3 by maintainers)
Commits related to this issue
- Update open source env Summary: We weren't exposing a mock for the batched bridge, which resulted in the WebSocket test failure but only in the open source copy of react-native. public Reviewed By:... — committed to facebook/react-native by cpojer 9 years ago
- TESTING [no merge] - workaround for facebook/jest#10221 Unless otherwise noted, this commit will be unnecessary when jacebook/jest#10221 is resolved. — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- deps: Use our fork of react-native. Trying this as a workaround for https://github.com/facebook/jest/issues/10221; see https://github.com/chrisbobbe/react-native/tree/pr-0.61.5-zulip for the contents... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- deps: Use our fork of react-native. Trying this as a workaround for https://github.com/facebook/jest/issues/10221; see https://github.com/chrisbobbe/react-native/tree/pr-0.61.5-zulip for the contents... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- Jest setup: Stop polyfilling Promise. It looks like this line was introduced in 3ff3987, in 2015, and it has remained in a similar form since then. I haven't found any explanation for it. At faceboo... — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- Jest setup: Stop polyfilling Promise. It looks like this line was introduced in 3ff3987, in 2015, and it has remained in a similar form since then. I haven't found any explanation for it. At faceboo... — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- Jest setup: Stop polyfilling Promise. It looks like this line was introduced in 3ff3987, in 2015, and it has remained in a similar form since then. I haven't found any explanation for it. At faceboo... — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- Jest setup: Stop polyfilling Promise. It looks like this line was introduced in 3ff3987, in 2015, and it has remained in a similar form since then. I haven't found any explanation for it. At faceboo... — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- Jest setup: Stop polyfilling Promise. It looks like this line was introduced in 3ff3987, in 2015, and it has remained in a similar form since then. I haven't found any explanation for it. At faceboo... — committed to chrisbobbe/react-native by chrisbobbe 4 years ago
- deps: Use our fork of react-native. Trying this as a workaround for https://github.com/facebook/jest/issues/10221; see https://github.com/chrisbobbe/react-native/tree/pr-0.61.5-zulip for the contents... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- deps: Use our fork of react-native. Trying this as a workaround for https://github.com/facebook/jest/issues/10221; see https://github.com/chrisbobbe/react-native/tree/pr-0.61.5-zulip for the contents... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier and less invasive than forking React Native, as we considered in zulip/react-native#5. The idea comes from Greg on a video call, with a cod... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. The idea comes from Greg on a video call, with a code example provided... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. The idea comes from Greg on a video call, with a code example provided... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. The idea comes from Greg on a video call, with a code example provided... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. The idea comes from Greg on a video call, with a code example provided... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. See https://github.com/facebook/jest/issues/10221#issuecomment-714335... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. See https://github.com/facebook/jest/issues/10221#issuecomment-714335... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- jest: Fix facebook/jest#10221 locally. This is much easier than forking React Native, as we considered in zulip/react-native#5. See https://github.com/facebook/jest/issues/10221#issuecomment-714335... — committed to chrisbobbe/zulip-mobile by chrisbobbe 4 years ago
- Adding legacy hint for useFakeTimers See this issue https://github.com/facebook/jest/issues/10221 for progress on support for the default 'modern' timer with react-native — committed to Andarius/react-native-reanimated by Andarius 2 years ago
I have the same error with nock 12.0.3 and jest 26.1.0
The test case
Failing :
Itās working when I replace
Is this moving along? As issue still persists.
I created a pull request to add the options object with an ignore property (notToFake) 100% inspired by the comment above. Any feedback is welcome and greatly appreciated.
The added code is kept deliberately simple and pragmatic, it can still be changed easily to reflect any input. https://github.com/facebook/jest/pull/11661
@tomdaniel0x01 thanks, I think I will use fake timers from sinon for now, as they seemed to work. But Iād would rather get rid of another dependency and use it in jest. Its the only I am really missing in jest.
I have to add to this issue for clarity: It does not only affect non-native global.promise replacements, it also makes native promises in Node.JS with TypeScript stop working.
@SimenB any progress on this? It seems like the problem and solution are both known. Is there any reason a change should not be made that would stop mocking
nextTick
? Is this just waiting on someone to make the change?Yepāwhen I do this, it works! Looks like https://github.com/sinonjs/fake-timers/pull/323 is related.
Thanks for the heads-up! Yeah, a priori, Iām not really interested in replacing
global.Promise
. But Iām using React Native, withpreset: 'react-native'
, so itās being done whether I like it or not (RN has been doing this since https://github.com/facebook/react-native/commit/3ff39870ce776c653823e1733363be0401896294). I suppose I could make a PR toreact-native
and fork it in the meantime, if it came to that.Hmm, I wonder if this is because we fake
process.nextTick
as well. As a test, can you modify yournode_modules/@jest/fake-timers/build/modernFakeTimers.js
(or wherever itās located) from the lineconst toFake = Object.keys(this._fakeTimers.timers);
toconst toFake = Object.keys(this._fakeTimers.timers).filter(name => name !== 'nextTick');
. That should exclude it. This is the default behavior of@sinonjs/fake-timers
, which Iāve changed in Jestās implementation.As an aside, one should never replace
global.Promise
- that suggestion is outdated. E.g. when usingasync-await
you will always get the nativePromise
regardless of the value ofglobal.Promise
.Finally fixed in #12572, available in https://github.com/facebook/jest/releases/tag/v28.0.0-alpha.8.
facing same issue. Have we fixed this issue or itās still in progress?
Or the other way : keep everything mocked by default, and a way to exclude some methods ?
Possible workaround for the time being is using https://github.com/sinonjs/fake-timers, as it was mention before. But hereās the example for those who stumbles across this thread:
Whatās the status? Will it be fixed?
@SimenB
So it would be acceptable to stop mocking
nextTick
by default, as long as there is a way to make sure it gets mocked? Would an API similar to sinonās for choosing what to mock work here?The userās code would then look something like this:
For most cases here I assume a strategically placed
jest.runAllTicks()
would solve the issue, but itās non-obviousThis, essentially š However, the change to make is not obvious. If we stop mocking
nextTick
by default we need to allow people to mock it if they want (right now we mock everything, which I think is ideal, but it sometimes breaks other modules).