(十一)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