# Tests use a fake clock when time is involved

- **Pattern:** `ab-000307` (`ai-slop-test-theater.test-operability.tests-use-fake-clock-for-time`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000307
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000307)

## Why it matters

Tests that call real `Date.now()`, `setTimeout`, or `setInterval` are non-deterministic — their behavior depends on how fast the CI machine is running, what other jobs are executing in parallel, and how close the system clock is to a boundary. A token-expiry check that takes 1ms on your laptop may take 150ms on an overloaded CI runner, making the test fail intermittently. Fake timers eliminate this class of flakiness by making the clock fully deterministic. ISO-25010:2011 testability requires that test outcomes be repeatable across environments.

## Severity rationale

Info because real-clock tests are annoying and flaky but don't represent a correctness gap — they still exercise the time-dependent code, just non-deterministically.

## Remediation

Use `vi.useFakeTimers()` in any test that imports or calls code that references `Date.now()`, `new Date()`, `setTimeout`, or `setInterval`. Always restore real timers with `vi.useRealTimers()` in an `afterEach` to avoid leaking fake timers into other tests:

```ts
import { describe, it, expect, vi, afterEach } from 'vitest'
import { isTokenExpired } from '@/lib/auth'

afterEach(() => vi.useRealTimers())

describe('isTokenExpired', () => {
  it('returns true when token is 1 hour old', () => {
    vi.useFakeTimers()
    vi.setSystemTime(new Date('2026-01-01T12:00:00Z'))
    const token = { issuedAt: new Date('2026-01-01T10:59:00Z').getTime() }
    expect(isTokenExpired(token)).toBe(true)
  })
})
```

## Detection

- **ID:** `tests-use-fake-clock-for-time`
- **Severity:** `info`
- **What to look for:** Walk all test files for tests that involve time-dependent code: tests that import a function whose implementation references `Date.now()`, `new Date()`, `setTimeout(`, `setInterval(`, `setImmediate(`. Count all such time-dependent tests. For each, verify it calls `vi.useFakeTimers()`/`jest.useFakeTimers()`/`sinon.useFakeTimers()` to control the clock.
- **Pass criteria:** At least 50% of time-dependent tests use fake timers. Report: "X time-dependent tests inspected, Y use fake timers."
- **Fail criteria:** Less than 50% of time-dependent tests use fake timers.
- **Skip (N/A) when:** No time-dependent tests detected.
- **Detail on fail:** `"4 time-dependent tests, only 1 uses fake timers. Tests rely on real wall-clock time, making them flaky."`
- **Remediation:** Real-clock tests are flaky on slow CI. Use fake timers for time-dependent code:

  ```ts
  // tests/debounce.test.ts
  import { describe, it, expect, vi } from 'vitest'

  describe('debounce', () => {
    it('coalesces rapid calls into one', () => {
      vi.useFakeTimers()
      const fn = vi.fn()
      const debounced = debounce(fn, 100)
      debounced()
      debounced()
      vi.advanceTimersByTime(100)
      expect(fn).toHaveBeenCalledTimes(1)
      vi.useRealTimers()
    })
  })
  ```

## External references

- iso-25010 maintainability.testability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-000307
