[jest] Guides - Timer Mocks

前言

在我们撰写jest的时候,常常会遇到source code的function有使用到setTimeout的情况,若是我们在jest中也使用setTimeout来模拟source code的真实timeout情况,那么一旦jest的数量变多时就会花费很多时间在等待timeout,所以jest就提供了fakeTimers的功能,他可以让使用者透过这个function达到自由控制时间(timer时间)的功能。

// 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()来开启fake timers功能,他模拟了setTimeout或其他timer的功能,若是在同一个jest中使用了多个fake timers则需要在beforeEach中再次设定jest.useFakeTimers(),否则timer不会重置。


Run All Timers

test('calls the callback after 1 second', () => {  const timerGame = require('../timerGame');  const callback = jest.fn();  timerGame(callback);  // At this point in time, the callback should not have been called yet  expect(callback).not.toBeCalled();  // Fast-forward until all timers have been executed  jest.runAllTimers();  // Now our callback should have been called!  expect(callback).toBeCalled();  expect(callback).toHaveBeenCalledTimes(1);});

以上面的例子来说,若我们需要若我们需要在一秒后呼叫这个callback function,所以我们使用jest的API(jest.runAllTimers())来加快timer的时间。

在还没使用jest.runAllTimers()时,callback function因为还在处于setTimeout阶段所以还没被执行,而当执行了jest的API那他就会将所有timer加快让他提前完成,所以下一行就可以看到callback function已经提前被呼叫了,这就是jest fake timer的功能。


Run Pending Timers

若你是使用resursive timer,因为他是在callback function中再设定一个timer,所以并不适合使用jest.runAllTimers(),这种情况可以使用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);  });});

由于source code中的function递迴的呼叫自身,并在自身中在建立一个setTimeout,所以我们在jest中呼叫这个function

刚进入这个function,他里面的setTimeout只有被呼叫一次(因为正在一秒的timeout阶段,所以还没呼叫10秒的setTimeout)。我们使用jest.runOnluPendingTimers()将这个一秒的timeout加速。因为加速了timer,所以可以马上呼叫第二的timer(10秒的setTimeout)。

Advance Timers by Time

我们也可以使用jest.advanceTimersByTime(msToRun)来指定需要加速多少毫秒。

// 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);  // At this point in time, the callback should not have been called yet  expect(callback).not.toBeCalled();  // Fast-forward 1000ms  jest.advanceTimersByTime(1000);  // Now our callback should have been called!  expect(callback).toBeCalled();  expect(callback).toHaveBeenCalledTimes(1);});

由于我们的source code中的timer设定为等待1秒,所以们可以指定将timer加速1s。


Clear Timers

在某些时候我们会需要在测试结束后清除所有的timers,所以jest也提供了jest.clearAllTimers()的API。

参考文献:
jest timer-mocks


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章