codecamp

Jest 计时器模拟

原生的定时器函数(如:​setTimeout​, ​setInterval​, ​clearTimeout​, ​clearInterval​)并不是很方便测试,因为程序需要等待相应的延时。 Jest可以将计时器和允许你控制时间流逝的函数计进行互换。

  1. // timerGame.js
  2. 'use strict';
  3. function timerGame(callback) {
  4. console.log('Ready....go!');
  5. setTimeout(() => {
  6. console.log("Time's up -- stop!");
  7. callback && callback();
  8. }, 1000);
  9. }
  10. module.exports = timerGame;
  1. // __tests__/timerGame-test.js
  2. 'use strict';
  3. jest.useFakeTimers();
  4. test('waits 1 second before ending the game', () => {
  5. const timerGame = require('../timerGame');
  6. timerGame();
  7. expect(setTimeout).toHaveBeenCalledTimes(1);
  8. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
  9. });

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

运行所有计时器

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

  1. test('calls the callback after 1 second', () => {
  2. const timerGame = require('../timerGame');
  3. const callback = jest.fn();
  4. timerGame(callback);
  5. // 在这个时间点,定时器的回调不应该被执行
  6. expect(callback).not.toBeCalled();
  7. // “快进”时间使得所有定时器回调被执行
  8. jest.runAllTimers();
  9. // 现在回调函数应该被调用了!
  10. expect(callback).toBeCalled();
  11. expect(callback).toHaveBeenCalledTimes(1);
  12. });

运行挂起的计时器

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

  1. // infiniteTimerGame.js
  2. 'use strict';
  3. function infiniteTimerGame(callback) {
  4. console.log('Ready....go!');
  5. setTimeout(() => {
  6. console.log("Time's up! 10 seconds before the next game starts...");
  7. callback && callback();
  8. // Schedule the next game in 10 seconds
  9. setTimeout(() => {
  10. infiniteTimerGame(callback);
  11. }, 10000);
  12. }, 1000);
  13. }
  14. module.exports = infiniteTimerGame;
  1. // __tests__/infiniteTimerGame-test.js
  2. 'use strict';
  3. jest.useFakeTimers();
  4. describe('infiniteTimerGame', () => {
  5. test('schedules a 10-second timer after 1 second', () => {
  6. const infiniteTimerGame = require('../infiniteTimerGame');
  7. const callback = jest.fn();
  8. infiniteTimerGame(callback);
  9. // At this point in time, there should have been a single call to
  10. // setTimeout to schedule the end of the game in 1 second.
  11. expect(setTimeout).toHaveBeenCalledTimes(1);
  12. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
  13. // Fast forward and exhaust only currently pending timers
  14. // (but not any new timers that get created during that process)
  15. jest.runOnlyPendingTimers();
  16. // At this point, our 1-second timer should have fired it's callback
  17. expect(callback).toBeCalled();
  18. // And it should have created a new timer to start the game over in
  19. // 10 seconds
  20. expect(setTimeout).toHaveBeenCalledTimes(2);
  21. expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
  22. });
  23. });

按时间提前计时器

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

另一种可选方式是使用 ​jeste. advancertimersbytime (msToRun)​。 当调用此API时,所有计时器都会以​msToRun​毫秒为单位提前。所有通过​setTimeout()​ 或​setInterval()​ 而处于任务队列中等待中的“宏任务”和一切其他应该在本时间片中被执行的东西都应该被执行。 此外,如果这些宏任务计划在同一时间段内执行的新宏任务,则将执行这些宏任务,直到队列中不再有应在​msToRun​毫秒内运行的宏任务为止。

  1. // timerGame.js
  2. 'use strict';
  3. function timerGame(callback) {
  4. console.log('Ready....go!');
  5. setTimeout(() => {
  6. console.log("Time's up -- stop!");
  7. callback && callback();
  8. }, 1000);
  9. }
  10. module.exports = timerGame;
  1. it('calls the callback after 1 second via advanceTimersByTime', () => {
  2. const timerGame = require('../timerGame');
  3. const callback = jest.fn();
  4. timerGame(callback);
  5. // 在这个时间点,回调函数不应该被执行
  6. expect(callback).not.toBeCalled();
  7. // “快进”时间,使得所有定时器回调都被执行
  8. jest.advanceTimersByTime(1000);
  9. // 到这里,所有的定时器回调都应该被执行了!
  10. expect(callback).toBeCalled();
  11. expect(callback).toHaveBeenCalledTimes(1);
  12. });

最后,在某些测试中,能够清除所有挂起的计时器有时可能很有用。为此,我们有​jest.clearAllTimers()​.


Jest 异步示例
Jest 手动模拟
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }