平時開發中我們經常用到定時器setInterval 或者setTimeout ,現在我們就寫一個定時器的測試用例代碼如下:
// demo.js
export const lazy = (fn)=> {
setTimeout(() => {
fn();
}, 3000);
}
// demo.test.js
import {lazy} from './timerDemo'
test('should call fn after 3s', () => {
const callback = jest.fn();
lazy(callback);
setTimeout(() => {
expect(callback).toBeCalled();
}, 3001);
})
使用npm run test運行測試用例,運行結果如下:

如何解決這個問題呢?
方法一:
之前的筆記有一個done回調函數(五)Jest測試異步代碼
此時我們利用done()來解決這個問題,代碼如下:
import {lazy} from './timerDemo'
test('should call fn after 3s', (done) => {
const callback = jest.fn();
lazy(callback);
setTimeout(() => {
expect(callback).toBeCalled();
done();
}, 3001);
})
方法二:
今天我們學習一種新的解決辦法,使用mock timer解決這個問題。jest 提供了mock timer 的功能,不要再使用真實的時間在這里等了,一個假的時間模擬一下就可以了。首先是jest.useFakeTimers() 的調用,它就告訴jest 在以后的測試中,可以使用假時間。當然只用它還不行,因為它只是表示可以使用,我們還要告訴jest在哪個地方使用,當jest 在測試的時候,到這個地方,它就自動使用假時間。兩個函數,jest.runAllTimers(), 它表示把所有時間都跑完。具體到我們這個測試,我們希望執完lazy(callback) 就調用, 把lazy函數中的3s時間立刻跑完。可以使用jest.runAllTimers();
具體代碼如下:
import {lazy} from './timerDemo'
jest.useFakeTimers();//可以使用假函數
test('should call fn after 3s', () => {
const callback = jest.fn();
lazy(callback);
jest.runAllTimers();//讓所有定時器立即執行
expect(callback).toBeCalled();
//expect(callback).toHaveBeenCalledTimes(1) 也可以使用
})
如果有多個定時器的時候,我們修改demo.js的代碼如下:
export const lazy = (fn)=> {
setTimeout(() => {
fn();
console.log('第一個定時器執行')
setTimeout(()=>{
console.log('第二個定時器執行')
},3000)
}, 3000);
}
此時運行測試用例的時候,兩個定時器會立馬都被執行掉。但如果我們只想運行最外層的那個定時器時,我們需要引入 runOnlyPendingTimers() ,只執行一個定時操作。具體代碼如下:
//demo.js
export const lazy = (fn)=> {
setTimeout(() => {
fn();
console.log('第一個定時器執行')
setTimeout(()=>{
console.log('第二個定時器執行')
},3000)
}, 3000);
}
//demo.test.js
import {lazy} from './timerDemo'
jest.useFakeTimers();//可以使用假函數
test('should call fn after 3s', () => {
const callback = jest.fn();
lazy(callback);
jest.runOnlyPendingTimers();//讓所有定時器立即執行
//expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1)
})
自己試着運行一下我們會發現,只有第一個執行了。
同時也可以使用 jest.advanceTimer() 快進幾秒。
具體代碼使用如下:
//demo.js
export const lazy = (fn)=> {
setTimeout(() => {
fn();
console.log('第一個定時器執行')
setTimeout(()=>{
console.log('第二個定時器執行')
},3000)
}, 3000);
}
//demo.test.js
import {lazy} from './timerDemo'
jest.useFakeTimers();//可以使用假函數
test('should call fn after 3s', () => {
const callback = jest.fn();
lazy(callback);
jest.advanceTimersByTime(3000)
expect(callback).toHaveBeenCalledTimes(1)
})
運行結果發現只執行了第一個定時器,如果還想執行第二個,我們可以修改demo.test.js代碼如下:
import {lazy} from './timerDemo'
jest.useFakeTimers();//可以使用假函數
test('should call fn after 3s', () => {
const callback = jest.fn();
lazy(callback);
jest.advanceTimersByTime(3000)
jest.advanceTimersByTime(3000)//第二個的時間以第一個的時間為基數
//expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1)
})
此時兩個定時器都會執行。 使用jest.advanceTimersByTime(n)快進n時間執行定時,多個advanceTimerByTime連用時,后一個會以前一個的時間為基點,如果不想互相影響,我們可以使用鈎子函數beforeEach解決這個問題。
前端交流群:掃碼進去

