Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fake all supported timers by default #323

Merged
merged 5 commits into from
Aug 25, 2024

Conversation

SimenB
Copy link
Member

@SimenB SimenB commented May 11, 2020

Purpose (TL;DR) - mandatory

It's very weird to me that this method is excluded from default mocking. I've read #126 and to me the solution seems to be for users to specify a toFake themselves, not force other people to specify "yes, when I say fake timers I do mean all timers".

Note that this should be considered a breaking change.

Whenever we start supporting some new timer, it should by default not be mocked, but I think when we later make a new major release it should be mocked by default again.

@@ -1262,10 +1262,7 @@ function withGlobal(_global) {
clock.methods = config.toFake || [];

if (clock.methods.length === 0) {
// do not fake nextTick by default - GitHub#126
clock.methods = keys(timers).filter(function(key) {
return key !== "nextTick" && key !== "queueMicrotask";
Copy link
Member Author

@SimenB SimenB May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems docs were lying 🤷 queueMicrotask was also not mocked by default

@codecov
Copy link

codecov bot commented May 11, 2020

Codecov Report

Merging #323 into master will decrease coverage by 0.03%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #323      +/-   ##
==========================================
- Coverage   92.75%   92.71%   -0.04%     
==========================================
  Files           1        1              
  Lines         552      549       -3     
==========================================
- Hits          512      509       -3     
  Misses         40       40              
Flag Coverage Δ
#unit 92.71% <100.00%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/fake-timers-src.js 92.71% <100.00%> (-0.04%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7f8322e...21d726f. Read the comment docs.

return key !== "nextTick" && key !== "queueMicrotask";
});
}
clock.methods = config.toFake || keys(timers);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another technically breaking change I guess - if people passed toFake: [] before, we faked everything. Seems like a bug fix to me to respect it, but maybe not 😀 If not, should just revert the last commit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think this and the other one should be a major version if we land them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can open a separate PR for it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can open a separate PR for it?

Yes

@benjamingr
Copy link
Member

So here's the thing: promise libraries and other libraries use nextTick and microticks for scheduling so this is a pretty big breaking change.

In principle I agree that we should mock this by default.

@@ -4504,19 +4504,6 @@ describe("FakeTimers", function() {
assert(called);
clock.uninstall();
});

it("does not install by default - GitHub#126", function(done) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to change this test to assert nextTick is mocked if the behavior changes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed the test above, I think that's fine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed the test above, I think that's fine?

Yes, this is fine. It's implicitly true, as the code won't work unless nextTick is synchronous.

@SimenB
Copy link
Member Author

SimenB commented May 11, 2020

So here's the thing: promise libraries and other libraries use nextTick and microticks for scheduling so this is a pretty big breaking change.

Good point, should probably call it out in the readme.

In principle I agree that we should mock this by default.

👍

@SimenB
Copy link
Member Author

SimenB commented May 11, 2020

@benjamingr added a section to the readme, feedback appreciated 😀

@benjamingr
Copy link
Member

@SimenB I am +1 on this change in principle but I know it will break pretty much every fake-timers install "in the wild" that uses the current API.

@SimenB
Copy link
Member Author

SimenB commented May 11, 2020

Should work if people use the builtin Promise and async-await, no? If it breaks builtin Promise global I'd consider that a bug in node.

My main issue is that to enable "fake everything" I have to enumerate all timers from the outside, which is not obvious I think. Maybe we could rather add a fakeNextTick explicit option (defaulting to false I guess) instead of piggy-backing toFake?

@benjamingr
Copy link
Member

Should work if people use the builtin Promise and async-await, no? If it breaks builtin Promise global I'd consider that a bug in node.

It doesn't break the built in promises but mostly because we don't mock its timings at the moment (we probably should with async_hooks). In practice this means that it's a lot harder than it has to to test promises with fake-timers (I recall a lot of discussions about this + async versions of timers somewhere?)

I don't mind seeing what breaks with mocking nextTick on some large codebases I maintain - though I assume I know the answer is "every place that relies on nextTick executing and doesn't .tick or runMicrotasks before that :]

@SimenB
Copy link
Member Author

SimenB commented May 11, 2020

As a data point, in a work project where I migrated to use Jest's fake timers now that Jest ships with Lolex it broke node-fetch (and/or integration with nock, not entirely sure, I just added a runMicrotasks call)

@stale
Copy link

stale bot commented Jul 18, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jul 18, 2020
@mroderick mroderick added pinned and removed stale labels Jul 21, 2020
@stale stale bot closed this Jul 25, 2020
@mroderick
Copy link
Member

Stale bot?!

@mroderick mroderick reopened this Jul 27, 2020
@fatso83
Copy link
Contributor

fatso83 commented Aug 13, 2020

This makes my head hurt and you guys know best anyhow. Not sure why it has stalled, though?

Copy link
Contributor

@fatso83 fatso83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this looks good. @SimenB anything you want addressed (apart from the conflicts)?

@fatso83
Copy link
Contributor

fatso83 commented May 28, 2021

B,B,B,B,bump? It's not super problematic to release a major version, as long as you also document how to deal with the fallout. Like using node-fetch as an example case, show what needs to be done to fix the test cases. @SimenB

return key !== "nextTick" && key !== "queueMicrotask";
});
}
clock.methods = config.toFake || keys(timers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can open a separate PR for it?

Yes

@@ -4504,19 +4504,6 @@ describe("FakeTimers", function() {
assert(called);
clock.uninstall();
});

it("does not install by default - GitHub#126", function(done) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed the test above, I think that's fine?

Yes, this is fine. It's implicitly true, as the code won't work unless nextTick is synchronous.

@mroderick
Copy link
Member

This needs a rebase to resolve the conflicts.

@SimenB do you have time to take this across the finish line? If not, would you be ok with someone else doing it?

@fatso83
Copy link
Contributor

fatso83 commented Feb 10, 2024

I want to finish this (and do #490) and release a new breaking major. Soonish

I don't mind seeing what breaks with mocking nextTick on some large codebases I maintain

There's been 4 years. I guess the number of places that rely on non-native Promises are a lot fewer and further between. Maybe it's not that bad? I think we would just need to add something to the changelog.

@fatso83
Copy link
Contributor

fatso83 commented Mar 22, 2024

Since I closed #490 and I haven't heard anything in months, I think I'll just pick this up and release a new breaking major.

@fatso83 fatso83 merged commit 5a4532a into sinonjs:main Aug 25, 2024
11 checks passed
@fatso83
Copy link
Contributor

fatso83 commented Aug 25, 2024

Weird how much you can get done in a weekend without kids 😄

@fatso83
Copy link
Contributor

fatso83 commented Nov 7, 2024

I think this needs to be reverted (even though I merged it myself in the end). Even in 2024, the new behavior of faking all timers seems to trip people up over and over again, ref sinonjs/sinon#2620 (comment), and so it makes for poor developer experience. nextTick and queueMicroTask is far less common to target than setTimeout and similar, but it's the latter crowd that now needs to supply a list of timers, instead of the more exotic crowd.

I'd rather just change the docs to say "fakes most timers by default, but you can also choose to stub all or a subset, if you so please" and then expose some constants they could use (DEFAULT_TIMERS,ES_STANDARD_TIMERS, ALL_TIMERS), a la:

fakeTimers.install({toFake: fakeTimers.ALL_TIMERS})

I think a library should aim to cover the common cases by default, allowing for the more exotic ones by config.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants