Testland
Browse all skills & agents

sinon-fake-timers-js

Wraps Sinon's @sinonjs/fake-timers library for JS/TS testing: install(), tick() / tickAsync(), setSystemTime(), restore(); covers timers (setTimeout / setInterval / requestAnimationFrame), Date / performance.now() / hrtime, and the toFake option for selective override. Use when testing JS/TS code with deterministic timer + clock behaviour. Composes dst-transition-reference + iso-8601-vs-rfc-3339-reference.

sinon-fake-timers-js

Overview

Sinon's @sinonjs/fake-timers is the canonical fake-timer + fake- clock library for JavaScript / TypeScript. Per sinonjs.org/releases/latest/fake-timers, it replaces the global timer functions and the Date constructor with controllable fakes.

This skill is for tests outside Jest (Jest has its own per jest-fake-timers).

When to use

  • Mocha / Jasmine / Vitest tests needing fake timers + clock.
  • Testing code that uses setTimeout / setInterval / Date.now / requestAnimationFrame.
  • Async-flow tests where promises must resolve at controlled ticks.

Authoring

Install

npm install --save-dev @sinonjs/fake-timers

Basic install

import FakeTimers from '@sinonjs/fake-timers';

const clock = FakeTimers.install({ now: new Date('2026-05-20T14:30:00Z').getTime() });

// Test code that calls Date.now() / new Date() / setTimeout / etc.
expect(new Date().toISOString()).toBe('2026-05-20T14:30:00.000Z');

clock.uninstall();

Tick forward

clock.tick(1000);                  // Advance 1000ms
expect(new Date().toISOString()).toBe('2026-05-20T14:30:01.000Z');

Async tick (for promise-based timers)

test('debounce fires after 300ms', async () => {
  let fired = false;
  setTimeout(() => { fired = true; }, 300);

  await clock.tickAsync(299);
  expect(fired).toBe(false);

  await clock.tickAsync(1);
  expect(fired).toBe(true);
});

Selectively fake

const clock = FakeTimers.install({
  now: new Date('2026-05-20T14:30:00Z').getTime(),
  toFake: ['setTimeout', 'setInterval', 'Date'],  // not 'performance', 'hrtime'
});

Useful when you want real performance.now() for benchmarking but fake Date.

setSystemTime

clock.setSystemTime(new Date('2027-01-01T00:00:00Z'));
expect(new Date().toISOString()).toBe('2027-01-01T00:00:00.000Z');

Jumps the clock without ticking any in-flight timers.

DST tests

// Spring-forward simulation requires careful zone setup
// Note: @sinonjs/fake-timers fakes UTC time; for local-zone DST
// behaviour, combine with process.env.TZ
process.env.TZ = 'America/New_York';
const clock = FakeTimers.install({
  now: new Date('2026-03-08T06:30:00Z').getTime(),  // 02:30 EDT — invalid local
});
// ... test that scheduling at 02:30 local degrades gracefully

Per dst-transition-reference: the test verifies behaviour at the transition; the fake clock makes it reproducible.

Teardown

afterEach(() => clock.uninstall());

Critical - leaked clocks contaminate subsequent tests.

Running

npx mocha
npx vitest run

CI integration

jobs:
  time-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v4
      - run: npm ci && npx vitest run

Anti-patterns

Anti-patternWhy it failsFix
Forget clock.uninstall()Leaked fake clock contaminates next testsafterEach(clock.uninstall)
Mix real + fake timers in same testRace conditionsEither fake everything or fake nothing
Use tick() for promise-resolving timersPromises don't resolve synchronouslyUse tickAsync
Hardcode Unix timestampsBrittle to system tzUse new Date(iso-string)
Test DST without process.env.TZUTC-only; DST tests degenerateSet TZ explicitly
Fake hrtime / performance alwaysLoses real-perf measurement when neededUse toFake option
Long tick(86400 * 365 * 1000) to "advance 1 year"Timers fire one-by-one; slowUse setSystemTime instead

Limitations

  • Doesn't fake monotonic time by default. performance.now() and process.hrtime() aren't faked unless requested.
  • DST handling depends on the JS runtime's tz library. Node uses ICU; browsers vary.
  • No leap-second simulation. See leap-second-reference.
  • Tests must isolate process.env.TZ per test. Setting it globally affects all subsequent tests.

References