Fake timers are paired with explicit clock control
Why it matters
Calling vi.useFakeTimers() or jest.useFakeTimers() freezes the JavaScript clock at the moment of the call. Any code that depends on setTimeout, setInterval, or Date.now() then hangs indefinitely — the timer callback never fires because the clock never advances. Tests that use useFakeTimers without a corresponding vi.advanceTimersByTime() or vi.runAllTimers() will either timeout or silently pass without actually executing the time-dependent code path. ISO-25010:2011 testability requires that all branches of code under test are actually exercised.
Severity rationale
Low because frozen-timer tests typically don't produce false passes — they usually hang or time out — but they do silently skip the time-dependent code branch.
Remediation
Always pair vi.useFakeTimers() with an explicit clock advancement, and restore real timers after the test. The test cannot verify timer-dependent behavior unless the clock is manually advanced:
it('debounces rapid input', () => {
vi.useFakeTimers()
const callback = vi.fn()
const debounced = debounce(callback, 100)
debounced()
debounced()
debounced()
vi.advanceTimersByTime(100) // fires the debounced callback
expect(callback).toHaveBeenCalledTimes(1)
vi.useRealTimers()
})
Detection
-
ID:
no-fake-timers-without-clock-control -
Severity:
low -
What to look for: Walk all test files. Count all
vi.useFakeTimers(/jest.useFakeTimers(/sinon.useFakeTimers(calls. For each, verify the same file (or test block) contains a corresponding clock-advancement call:vi.advanceTimersByTime(/jest.advanceTimersByTime(/clock.tick(/vi.runAllTimers(/jest.runAllTimers(. AuseFakeTimerswithout advancement freezes the clock — any code that waits for a timer never resolves. -
Pass criteria: 100% of
useFakeTimerscalls have a corresponding clock-advancement call in the same test block. Report: "X useFakeTimers calls, Y with clock advancement, 0 frozen." -
Fail criteria: At least 1
useFakeTimerscall has no clock-advancement. -
Skip (N/A) when: No
useFakeTimerscalls in any test file. -
Cross-reference: For broader test framework patterns, the Code Quality Essentials audit (
code-quality-essentials) covers test setup conventions. -
Detail on fail:
"1 frozen fake timer: tests/debounce.test.ts calls vi.useFakeTimers() but never calls vi.advanceTimersByTime() — debounced code under test never fires" -
Remediation:
vi.useFakeTimers()freezes the clock — any code that usessetTimeout/setInterval/Date.now()will never advance. Pair it withvi.advanceTimersByTime():it('debounces input', () => { vi.useFakeTimers() const callback = vi.fn() const debounced = debounce(callback, 100) debounced() debounced() debounced() vi.advanceTimersByTime(100) // advance the clock expect(callback).toHaveBeenCalledTimes(1) vi.useRealTimers() })
External references
- iso-25010:2011 · maintainability.testability — Testability (maintainability sub-characteristic)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-slop-test-theater·automated