Jest 計(jì)時(shí)器模擬

2021-09-18 20:22 更新

原生的定時(shí)器函數(shù)(如:?setTimeout?, ?setInterval?, ?clearTimeout?, ?clearInterval?)并不是很方便測試,因?yàn)槌绦蛐枰却鄳?yīng)的延時(shí)。 Jest可以將計(jì)時(shí)器和允許你控制時(shí)間流逝的函數(shù)計(jì)進(jìn)行互換。

  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()?;?來模擬定時(shí)器函數(shù)。 通過mock函數(shù)可以模擬setTimeout和其他的定時(shí)器函數(shù)。 如果你需要在一個(gè)文件或一個(gè)?describe?塊中運(yùn)行多次測試,可以在每次測試前手動(dòng)添加?jest.useFakeTimers();?,或者在?beforeEach?中添加。 如果不這樣做的話將導(dǎo)致內(nèi)部的定時(shí)器不被重置。

運(yùn)行所有計(jì)時(shí)器

對于這個(gè)模塊我們還需要寫一個(gè)測試,用于判斷回調(diào)函數(shù)是否在1秒后被調(diào)用的。 為此,我們將使用Jest的定時(shí)器控制API,用于在測試中將時(shí)間“快進(jìn)”到正確的時(shí)間點(diǎn)。

  1. test('calls the callback after 1 second', () => {
  2. const timerGame = require('../timerGame');
  3. const callback = jest.fn();
  4. timerGame(callback);
  5. // 在這個(gè)時(shí)間點(diǎn),定時(shí)器的回調(diào)不應(yīng)該被執(zhí)行
  6. expect(callback).not.toBeCalled();
  7. // “快進(jìn)”時(shí)間使得所有定時(shí)器回調(diào)被執(zhí)行
  8. jest.runAllTimers();
  9. // 現(xiàn)在回調(diào)函數(shù)應(yīng)該被調(diào)用了!
  10. expect(callback).toBeCalled();
  11. expect(callback).toHaveBeenCalledTimes(1);
  12. });

運(yùn)行掛起的計(jì)時(shí)器

在某些場景下你可能還需要“循環(huán)定時(shí)器”——在定時(shí)器的callback函數(shù)中再次設(shè)置一個(gè)新定時(shí)器。 對于這種情況,如果將定時(shí)器一直運(yùn)行下去那將陷入死循環(huán),所以在此場景下不應(yīng)該使用?jest.runAllTimers()?,因?yàn)檫@些原因,你可以使用 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. });

按時(shí)間提前計(jì)時(shí)器

從22.0.0版本的Jest開始,?runTimersToTime?被重命名為?advanceTimersByTime?。

另一種可選方式是使用 ?jeste. advancertimersbytime (msToRun)?。 當(dāng)調(diào)用此API時(shí),所有計(jì)時(shí)器都會(huì)以?msToRun?毫秒為單位提前。所有通過?setTimeout()? 或?setInterval()? 而處于任務(wù)隊(duì)列中等待中的“宏任務(wù)”和一切其他應(yīng)該在本時(shí)間片中被執(zhí)行的東西都應(yīng)該被執(zhí)行。 此外,如果這些宏任務(wù)計(jì)劃在同一時(shí)間段內(nèi)執(zhí)行的新宏任務(wù),則將執(zhí)行這些宏任務(wù),直到隊(duì)列中不再有應(yīng)在?msToRun?毫秒內(nèi)運(yùn)行的宏任務(wù)為止。

  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. // 在這個(gè)時(shí)間點(diǎn),回調(diào)函數(shù)不應(yīng)該被執(zhí)行
  6. expect(callback).not.toBeCalled();
  7. // “快進(jìn)”時(shí)間,使得所有定時(shí)器回調(diào)都被執(zhí)行
  8. jest.advanceTimersByTime(1000);
  9. // 到這里,所有的定時(shí)器回調(diào)都應(yīng)該被執(zhí)行了!
  10. expect(callback).toBeCalled();
  11. expect(callback).toHaveBeenCalledTimes(1);
  12. });

最后,在某些測試中,能夠清除所有掛起的計(jì)時(shí)器有時(shí)可能很有用。為此,我們有?jest.clearAllTimers()?.


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號