当前位置: 首页 > 文档资料 > Jest 中文文档 >

Timer Mocks

优质
小牛编辑
129浏览
2023-12-01

原生的定时器函数(如:setTimeout, setInterval, clearTimeout, clearInterval)并不是很方便测试,因为程序需要等待相应的延时。 Jest can swap out timers with functions that allow you to control the passage of time. Great Scott!

// timerGame.js
'use strict';

function timerGame(callback) {
  console.log('Ready....go!');
  setTimeout(() => {
    console.log("Time's up -- stop!");
    callback && callback();
  }, 1000);
}

module.exports = timerGame;
// __tests__/timerGame-test.js
'use strict';

jest.useFakeTimers();

test('waits 1 second before ending the game', () => {
  const timerGame = require('../timerGame');
  timerGame();

  expect(setTimeout).toHaveBeenCalledTimes(1);
  expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
});

在这里我们通过jest.useFakeTimers();来模拟定时器函数。 通过mock函数可以模拟setTimeout和其他的定时器函数。 如果你需要在一个文件或一个describe块中运行多次测试,可以在每次测试前手动添加jest.useFakeTimers();,或者在beforeEach中添加。 如果不这样做的话将导致内部的定时器不被重置。

Run All Timers

对于这个模块我们还需要写一个测试,用于判断回调函数是否在1秒后被调用的。 为此,我们将使用Jest的定时器控制API,用于在测试中将时间“快进”到正确的时间点。

test('calls the callback after 1 second', () => {
  const timerGame = require('../timerGame');
  const callback = jest.fn();

  timerGame(callback);

  // 在这个时间点,定时器的回调不应该被执行
  expect(callback).not.toBeCalled();

  // “快进”时间使得所有定时器回调被执行
  jest.runAllTimers();

  // 现在回调函数应该被调用了!
  expect(callback).toBeCalled();
  expect(callback).toHaveBeenCalledTimes(1);
});

Run Pending Timers

在某些场景下你可能还需要“循环定时器”——在定时器的callback函数中再次设置一个新定时器。 对于这种情况,如果将定时器一直运行下去那将陷入死循环,所以在此场景下不应该使用jest.runAllTimers() For these cases you might use jest.runOnlyPendingTimers():

// infiniteTimerGame.js
'use strict';

function infiniteTimerGame(callback) {
  console.log('Ready....go!');

  setTimeout(() => {
    console.log("Time's up! 10 seconds before the next game starts...");
    callback && callback();

    // Schedule the next game in 10 seconds
    setTimeout(() => {
      infiniteTimerGame(callback);
    }, 10000);
  }, 1000);
}

module.exports = infiniteTimerGame;
// __tests__/infiniteTimerGame-test.js
'use strict';

jest.useFakeTimers();

describe('infiniteTimerGame', () => {
  test('schedules a 10-second timer after 1 second', () => {
    const infiniteTimerGame = require('../infiniteTimerGame');
    const callback = jest.fn();

    infiniteTimerGame(callback);

    // At this point in time, there should have been a single call to
    // setTimeout to schedule the end of the game in 1 second.
    expect(setTimeout).toHaveBeenCalledTimes(1);
    expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);

    // Fast forward and exhaust only currently pending timers
    // (but not any new timers that get created during that process)
    jest.runOnlyPendingTimers();

    // At this point, our 1-second timer should have fired it's callback
    expect(callback).toBeCalled();

    // And it should have created a new timer to start the game over in
    // 10 seconds
    expect(setTimeout).toHaveBeenCalledTimes(2);
    expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
  });
});

Advance Timers by Time

22.0.0版本的Jest开始,runTimersToTime被重命名为advanceTimersByTime

另一种可选方式是使用 jeste. advancertimersbytime (msToRun)。 When this API is called, all timers are advanced by msToRun milliseconds. 所有通过setTimeout() 或setInterval() 而处于任务队列中等待中的“宏任务”和一切其他应该在本时间片中被执行的东西都应该被执行。 Additionally if those macro-tasks schedule new macro-tasks that would be executed within the same time frame, those will be executed until there are no more macro-tasks remaining in the queue that should be run within msToRun milliseconds.

// timerGame.js
'use strict';

function timerGame(callback) {
  console.log('Ready....go!');
  setTimeout(() => {
    console.log("Time's up -- stop!");
    callback && callback();
  }, 1000);
}

module.exports = timerGame;
it('calls the callback after 1 second via advanceTimersByTime', () => {
  const timerGame = require('../timerGame');
  const callback = jest.fn();

  timerGame(callback);

  // 在这个时间点,回调函数不应该被执行
  expect(callback).not.toBeCalled();

  // “快进”时间,使得所有定时器回调都被执行
  jest.advanceTimersByTime(1000);

  // 到这里,所有的定时器回调都应该被执行了!
  expect(callback).toBeCalled();
  expect(callback).toHaveBeenCalledTimes(1);
});

Lastly, it may occasionally be useful in some tests to be able to clear all of the pending timers. For this, we have jest.clearAllTimers().

这个例子的代码位于:examples/timer