原生的定時(shí)器函數(shù)(如:?setTimeout
?, ?setInterval
?, ?clearTimeout
?, ?clearInterval
?)并不是很方便測試,因?yàn)槌绦蛐枰却鄳?yīng)的延時(shí)。 Jest可以將計(jì)時(shí)器和允許你控制時(shí)間流逝的函數(shù)計(jì)進(jìn)行互換。
// 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);
});
在這里我們通過??
?來模擬定時(shí)器函數(shù)。 通過mock函數(shù)可以模擬setTimeout和其他的定時(shí)器函數(shù)。 如果你需要在一個(gè)文件或一個(gè)?jest.useFakeTimers()
?;describe
?塊中運(yùn)行多次測試,可以在每次測試前手動(dòng)添加?jest.useFakeTimers();
?,或者在?beforeEach
?中添加。 如果不這樣做的話將導(dǎo)致內(nèi)部的定時(shí)器不被重置。
對于這個(gè)模塊我們還需要寫一個(gè)測試,用于判斷回調(diào)函數(shù)是否在1秒后被調(diào)用的。 為此,我們將使用Jest的定時(shí)器控制API,用于在測試中將時(shí)間“快進(jìn)”到正確的時(shí)間點(diǎn)。
test('calls the callback after 1 second', () => {
const timerGame = require('../timerGame');
const callback = jest.fn();
timerGame(callback);
// 在這個(gè)時(shí)間點(diǎn),定時(shí)器的回調(diào)不應(yīng)該被執(zhí)行
expect(callback).not.toBeCalled();
// “快進(jìn)”時(shí)間使得所有定時(shí)器回調(diào)被執(zhí)行
jest.runAllTimers();
// 現(xiàn)在回調(diào)函數(shù)應(yīng)該被調(diào)用了!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
在某些場景下你可能還需要“循環(huán)定時(shí)器”——在定時(shí)器的callback函數(shù)中再次設(shè)置一個(gè)新定時(shí)器。 對于這種情況,如果將定時(shí)器一直運(yùn)行下去那將陷入死循環(huán),所以在此場景下不應(yīng)該使用?jest.runAllTimers()
?,因?yàn)檫@些原因,你可以使用 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);
});
});
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ù)為止。
// 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);
// 在這個(gè)時(shí)間點(diǎn),回調(diào)函數(shù)不應(yīng)該被執(zhí)行
expect(callback).not.toBeCalled();
// “快進(jìn)”時(shí)間,使得所有定時(shí)器回調(diào)都被執(zhí)行
jest.advanceTimersByTime(1000);
// 到這里,所有的定時(shí)器回調(diào)都應(yīng)該被執(zhí)行了!
expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});
最后,在某些測試中,能夠清除所有掛起的計(jì)時(shí)器有時(shí)可能很有用。為此,我們有?jest.clearAllTimers()
?.
更多建議: