close
  • 简体中文
  • Fake timers

    当你的代码中设置了很长的定时器(timeout),而你又不想在测试中等待它们时,fake timers 会非常有用。

    Rstest 提供了一些实用函数,基于 @sinonjs/fake-timers 实现定时器的模拟。

    rs.useFakeTimers

    • 别名: rstest.useFakeTimers
    • 类型: (config?: FakeTimerInstallOpts) => RstestUtilities & Disposable

    调用此方法可以启用定时器的模拟。底层使用 @sinonjs/fake-timers

    rs.useFakeTimers();

    你也可以传递配置对象以自定义 fake timers 的行为。

    该配置会透传给 @sinonjs/fake-timers。例如,toFaketoNotFake 用于控制要模拟哪些定时器 API,now 用于设置初始的模拟系统时间,shouldAdvanceTime / advanceTimeDelta 用于开启自动推进。

    Disposable / using 语法

    Rstest 支持使用 using 语法在代码块退出时自动恢复真实定时器:

    {
      using _timers = rs.useFakeTimers();
      // Fake timers 在这个代码块中启用。
    }

    rs.useRealTimers

    • 别名: rstest.useRealTimers
    • 类型: () => RstestUtilities

    恢复原生的定时器函数(如 setTimeoutsetInterval 等),关闭 fake timers。

    rs.useRealTimers();

    rs.isFakeTimers

    • 别名: rstest.isFakeTimers
    • 类型: () => boolean

    如果当前启用了 fake timers,则返回 true,否则返回 false

    if (rs.isFakeTimers()) {
      // Fake timers 已启用
    }

    rs.setSystemTime

    • 别名: rstest.setSystemTime
    • 类型: (now?: number | Date | { epochMilliseconds: number }) => RstestUtilities

    设置 fake timers 使用的当前系统时间。适用于需要测试依赖当前日期或时间的代码。

    启用 fake timers 时,该方法会模拟用户修改系统时钟(影响 Dateperformance.now 等与日期相关的 API),但不会触发任何 timer。若未启用 fake timers,则仅 mock Date.* 调用,因此无需先调用 rs.useFakeTimers 即可固定时钟。

    now 也可以是类似 Temporal 的对象,例如 Temporal.InstantTemporal.ZonedDateTime

    rs.setSystemTime(new Date('2020-01-01T00:00:00Z'));
    
    expect(new Date().toISOString()).toBe('2020-01-01T00:00:00.000Z');
    
    rs.useRealTimers();

    rs.getRealSystemTime

    • 别名: rstest.getRealSystemTime
    • 类型: () => number

    即使在启用 fake timers 时,也可以返回真实系统时间(时间戳)。

    const realTime = rs.getRealSystemTime();

    rs.runAllTicks

    • 别名: rstest.runAllTicks
    • 类型: () => RstestUtilities

    运行所有已排队的微任务(如 process.nextTick)。

    rs.runAllTimers

    • 别名: rstest.runAllTimers
    • 类型: () => RstestUtilities

    执行所有待运行的定时器(包括 timeout 和 interval)。

    rs.runAllTimersAsync

    • 别名: rstest.runAllTimersAsync
    • 类型: () => Promise<RstestUtilities>

    异步执行所有待运行的定时器。

    rs.runOnlyPendingTimers

    • 别名: rstest.runOnlyPendingTimers
    • 类型: () => RstestUtilities

    只运行当前待运行的定时器(不会调度新的定时器)。

    rs.runOnlyPendingTimersAsync

    • 别名: rstest.runOnlyPendingTimersAsync
    • 类型: () => Promise<RstestUtilities>

    异步只运行当前待运行的定时器。

    rs.advanceTimersByTime

    • 别名: rstest.advanceTimersByTime
    • 类型: (ms: number | string | Temporal.Duration) => RstestUtilities

    将 fake timers 快进指定的毫秒数,并执行在此期间计划的所有定时器。

    字符串时长使用与 @sinonjs/fake-timers 相同的格式,例如 '00:10' 表示 10 秒。

    rs.advanceTimersByTimeAsync

    • 别名: rstest.advanceTimersByTimeAsync
    • 类型: (ms: number | string | Temporal.Duration) => Promise<RstestUtilities>

    异步快进 fake timers 指定的毫秒数。

    rs.advanceTimersToNextTimer

    • 别名: rstest.advanceTimersToNextTimer
    • 类型: (steps?: number) => RstestUtilities

    将定时器推进到下一个计划的定时器,可选地指定推进的步数。

    rs.advanceTimersToNextTimerAsync

    • 别名: rstest.advanceTimersToNextTimerAsync
    • 类型: (steps?: number) => Promise<RstestUtilities>

    异步将定时器推进到下一个计划的定时器。

    rs.advanceTimersToNextFrame

    • 别名: rstest.advanceTimersToNextFrame
    • 类型: () => RstestUtilities

    将定时器推进到下一个动画帧。

    rs.jumpTimersByTime

    • 别名: rstest.jumpTimersByTime
    • 类型: (ms: number | string | Temporal.Duration) => RstestUtilities

    将模拟时钟直接跳过指定时间,每个受影响的定时器回调最多只会触发一次。它适用于模拟 JavaScript 引擎被挂起后恢复的场景,此时中间的 interval tick 会被跳过。

    rs.useFakeTimers({ now: 0 });
    
    const cb = rs.fn();
    setInterval(cb, 1000);
    
    rs.jumpTimersByTime(5000);
    
    expect(cb).toHaveBeenCalledTimes(1);

    rs.setTickMode

    • 别名: rstest.setTickMode
    • 类型: (mode: { mode: 'manual' | 'nextAsync' } | { mode: 'interval'; delta?: number }) => RstestUtilities

    配置 fake timers 自动推进时间的方式。

    • { mode: 'manual' }:只有调用 rs.advanceTimersByTime() 等 API 时,定时器才会推进。
    • { mode: 'nextAsync' }:时钟会持续推进到下一个计划的定时器,并在定时器之间让 Promise 回调运行。
    • { mode: 'interval', delta?: number }:按间隔推进模拟时间,等价于 shouldAdvanceTime / advanceTimeDelta
    rs.useFakeTimers();
    rs.setTickMode({ mode: 'nextAsync' });
    
    await new Promise((resolve) => setTimeout(resolve, 1000));

    rs.getTimerCount

    • 别名: rstest.getTimerCount
    • 类型: () => number

    返回当前 fake timers 中还剩多少个待运行的定时器。

    const count = rs.getTimerCount();

    rs.clearAllTimers

    • 别名: rstest.clearAllTimers
    • 类型: () => RstestUtilities

    移除所有已计划但尚未执行的定时器。

    rs.clearAllTimers();