← Concepts

阅读中文版本

Time Travel Testing

Time travel testing swaps the real clock for a controllable one — advancing instantly past retries, subscriptions, and expiry — making temporal behavior deterministic and testable at full speed.

testing

Definition

Time travel testing replaces ambient time with an injectable clock: freeze it for repeatability, advance it programmatically to cross deadlines and TTLs, and assert behavior at chosen instants. It covers retry schedules, token expiry, rate limit windows, scheduled jobs, and timezone boundaries without sleeping — suites run at machine speed instead of waiting for wall time.

Context and scope

Implemented with fake timers in test frameworks, clock injection in application design, and TTL manipulation in caches. Requires disciplined clock access: new Date and Date.now called directly resist interception.

Common misconceptions

  • Injecting a clock is overengineering — direct clock calls are exactly why time-dependent tests flake and cannot be fast-forwarded
  • Sleeping in tests approximates it — sleeps cost the full duration and still race; virtual time is instantaneous and deterministic
  • Freezing time is enough — many bugs live in transitions: crossing an expiry, DST boundary, or month rollover requires advancing, not freezing

Anti-patterns

  • Calling Date.now deep inside business logic and being unable to control time from tests
  • Testing DST and month-end boundaries only with real dates and hitting them once a year in production
  • Advancing fake time while real timers still fire, mixing two clocks in one test

Related concepts

Related examples

Authoritative source

https://vitest.dev/api/vi.html

What it is

Time-dependent behavior — retries, expiry, scheduling — is real logic that deserves real tests. Wall-clock time makes those tests slow and nondeterministic. A controllable clock makes them instant and exact.

How it works

The application reads time through an injected interface. Tests substitute a fake: set the current instant, advance by deltas, and fire scheduled callbacks in order. Framework fakes intercept setTimeout and setInterval so pending timers execute on advance. Real-time dependencies — NTP, file timestamps, database now() — need their own seams.

When to use it

Any logic reading the clock: subscription renewal, cache and token TTL, backoff and jitter schedules, cron-like triggers, timezone conversions. Boundary tables — leap years, DST switches, month ends — become ordinary test inputs rather than annual production experiments.

Pitfalls

Partial interception mixes clock domains and produces surreal failures. Instant-advance hides ordering issues that real time exposes — pair with occasional real-clock soak tests. Distributed time — multiple nodes, skew — needs simulated clock services rather than one local fake.