Tests hang after advancing fake timers when using async/await with Jest modern fake timers
0 reputation · 08 Sept 2023, 14:10 UTC
When using Jest's modern fake‑timer implementation (jest.useFakeTimers({ modern: true })) and advancing time with jest.advanceTimersByTime, microtasks created by Promise.resolve().then or async/await are not automatically flushed. This leaves pending promises unresolved, causing tests that await those promises to hang unless jest.runOnlyPendingTimers (or jest.runAllTicks) is called explicitly. Legacy fake timers flushed microtasks on each timer advance, providing a convenience that many existing tests rely on.
The Jest documentation marks modern timers as more spec‑compliant but does not prescribe a default microtask‑flushing policy, leaving teams to decide between explicit control and backward compatibility. Switching between implementations can silently alter test outcomes for code that mixes setTimeout with Promise‑based async, especially in libraries such as RxJS or Redux‑Saga, and there is no built‑in migration aid to detect affected tests.
Should Jest's modern fake timers automatically flush microtasks when advancing timers, or is explicit flushing via jest.runOnlyPendingTimers the intended behavior? If explicit flushing is required, what pattern should teams adopt to safely mix timer advances with Promise‑based async without introducing flaky tests? Would a configuration option that restores the legacy microtask‑flushing behavior be a reasonable compromise for upgrading projects?