(十一)Jest 中的 mock timer


平時開發中我們經常用到定時器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解決這個問題。

 前端交流群:掃碼進去

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM