Skip to main content
Bun provides fake timers to control time-based operations in your tests. This lets you test timeout, interval, and time-dependent code without waiting for real time to pass.

Basic usage

Use jest.useFakeTimers() to enable fake timers:
import { test, expect, jest } from "bun:test";

test("setTimeout with fake timers", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setTimeout(callback, 1000);
  
  // No time has passed yet
  expect(callback).not.toHaveBeenCalled();
  
  // Fast-forward 1 second
  jest.advanceTimersByTime(1000);
  
  expect(callback).toHaveBeenCalled();
});

Timer APIs

jest.useFakeTimers()

Enables fake timers. All timer APIs will be mocked:
  • setTimeout
  • setInterval
  • setImmediate
  • Date.now()
  • new Date()
import { jest } from "bun:test";

jest.useFakeTimers();

jest.useRealTimers()

Restores real timers:
import { jest } from "bun:test";

jest.useFakeTimers();
// ... tests with fake timers
jest.useRealTimers();
// ... tests with real timers

jest.advanceTimersByTime(ms)

Advances all timers by the specified milliseconds:
import { test, expect, jest } from "bun:test";

test("advance timers", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setTimeout(callback, 1000);
  
  jest.advanceTimersByTime(500);
  expect(callback).not.toHaveBeenCalled();
  
  jest.advanceTimersByTime(500);
  expect(callback).toHaveBeenCalled();
});

jest.runAllTimers()

Runs all pending timers until there are no more:
import { test, expect, jest } from "bun:test";

test("run all timers", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setTimeout(callback, 1000);
  setTimeout(callback, 2000);
  
  jest.runAllTimers();
  
  expect(callback).toHaveBeenCalledTimes(2);
});
runAllTimers() will cause an infinite loop if you have recurring timers (like setInterval).

jest.runOnlyPendingTimers()

Runs only the timers that are currently pending:
import { test, expect, jest } from "bun:test";

test("run only pending timers", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn(() => {
    // This creates a new timer
    setTimeout(callback, 1000);
  });
  
  setTimeout(callback, 1000);
  
  // Runs only the first timer, not the one created in the callback
  jest.runOnlyPendingTimers();
  
  expect(callback).toHaveBeenCalledTimes(1);
});

jest.advanceTimersToNextTimer()

Advances timers to the next timer:
import { test, expect, jest } from "bun:test";

test("advance to next timer", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setTimeout(callback, 100);
  setTimeout(callback, 200);
  
  jest.advanceTimersToNextTimer();
  expect(callback).toHaveBeenCalledTimes(1);
  
  jest.advanceTimersToNextTimer();
  expect(callback).toHaveBeenCalledTimes(2);
});

jest.clearAllTimers()

Clears all pending timers:
import { test, expect, jest } from "bun:test";

test("clear all timers", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setTimeout(callback, 1000);
  
  jest.clearAllTimers();
  jest.runAllTimers();
  
  expect(callback).not.toHaveBeenCalled();
});

jest.getTimerCount()

Returns the number of pending timers:
import { test, expect, jest } from "bun:test";

test("get timer count", () => {
  jest.useFakeTimers();
  
  setTimeout(() => {}, 1000);
  setTimeout(() => {}, 2000);
  
  expect(jest.getTimerCount()).toBe(2);
  
  jest.advanceTimersToNextTimer();
  expect(jest.getTimerCount()).toBe(1);
});

Mocking Date

Fake timers also mock Date:
import { test, expect, jest } from "bun:test";

test("mock Date", () => {
  jest.useFakeTimers();
  jest.setSystemTime(new Date("2024-01-01"));
  
  expect(Date.now()).toBe(new Date("2024-01-01").getTime());
  expect(new Date().toISOString()).toBe("2024-01-01T00:00:00.000Z");
  
  jest.advanceTimersByTime(1000);
  expect(Date.now()).toBe(new Date("2024-01-01").getTime() + 1000);
});

jest.setSystemTime()

Sets the system time:
import { jest } from "bun:test";

jest.useFakeTimers();
jest.setSystemTime(new Date("2024-01-01"));
You can also pass a timestamp:
jest.setSystemTime(1704067200000);

jest.getRealSystemTime()

Gets the real system time (even with fake timers enabled):
import { test, expect, jest } from "bun:test";

test("get real system time", () => {
  jest.useFakeTimers();
  jest.setSystemTime(new Date("2024-01-01"));
  
  expect(Date.now()).toBe(new Date("2024-01-01").getTime());
  expect(jest.getRealSystemTime()).toBeGreaterThan(new Date("2024-01-01").getTime());
});

Testing intervals

import { test, expect, jest } from "bun:test";

test("setInterval", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  setInterval(callback, 1000);
  
  jest.advanceTimersByTime(2500);
  
  expect(callback).toHaveBeenCalledTimes(2);
  
  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledTimes(3);
});

Testing animations

Test requestAnimationFrame:
import { test, expect, jest } from "bun:test";

test("requestAnimationFrame", () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  requestAnimationFrame(callback);
  
  // Advance by one frame (typically 16ms)
  jest.advanceTimersByTime(16);
  
  expect(callback).toHaveBeenCalled();
});

Cleanup

Always restore real timers after tests:
import { afterEach, jest } from "bun:test";

afterEach(() => {
  jest.useRealTimers();
});
Or use beforeEach and afterEach:
import { beforeEach, afterEach, jest } from "bun:test";

beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

Real-world example

Testing a debounce function:
import { test, expect, jest, beforeEach, afterEach } from "bun:test";

function debounce(fn: Function, delay: number) {
  let timeoutId: Timer;
  return (...args: any[]) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}

beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

test("debounce delays execution", () => {
  const callback = jest.fn();
  const debounced = debounce(callback, 1000);
  
  debounced("arg1");
  expect(callback).not.toHaveBeenCalled();
  
  jest.advanceTimersByTime(999);
  expect(callback).not.toHaveBeenCalled();
  
  jest.advanceTimersByTime(1);
  expect(callback).toHaveBeenCalledWith("arg1");
});

test("debounce cancels previous calls", () => {
  const callback = jest.fn();
  const debounced = debounce(callback, 1000);
  
  debounced("arg1");
  jest.advanceTimersByTime(500);
  
  debounced("arg2");
  jest.advanceTimersByTime(500);
  
  // First call was cancelled
  expect(callback).not.toHaveBeenCalled();
  
  jest.advanceTimersByTime(500);
  expect(callback).toHaveBeenCalledTimes(1);
  expect(callback).toHaveBeenCalledWith("arg2");
});

Vitest compatibility

Bun also supports Vitest’s timer APIs:
import { vi } from "bun:test";

vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.runAllTimers();
vi.useRealTimers();
All jest.* timer methods are available as vi.* as well.

Build docs developers (and LLMs) love