跳转到主要内容
版本:25.倍

定时器模拟

本机计时器函数(即,setTimeout,setInterval,clearTimeout,clearInterval)对于测试环境来说不是很理想,因为它们依赖于实时时间。Jest可以用允许您控制时间流逝的函数替换计时器。伟大的斯科特!

/ / timerGame.js
使用严格的 ;
函数 timerGame ( 回调 ) {
控制台 日志 ( “准备好了……走吧!” ) ;
setTimeout ( ( ) = > {
控制台 日志 ( “时间到了——停!” ) ;
回调 & & 回调 ( ) ;
} , 1000 ) ;
}
模块 出口 = timerGame ;
/ / __tests__ / timerGame-test.js
使用严格的 ;
开玩笑 useFakeTimers ( ) ;
测试 ( “在结束游戏前等待1秒” , ( ) = > {
常量 timerGame = 需要 ( “. . / timerGame” ) ;
timerGame ( ) ;
预计 ( setTimeout ) toHaveBeenCalledTimes ( 1 ) ;
预计 ( setTimeout ) toHaveBeenLastCalledWith ( 预计 任何 ( 函数 ) , 1000 ) ;
} ) ;

这里我们通过调用来启用假计时器jest.useFakeTimers ();。这将使用模拟函数模拟setTimeout和其他计时器函数。如果在一个文件或描述块中运行多个测试,jest.useFakeTimers ();可以在每次测试之前手动调用,或者使用诸如beforeEach。不这样做将导致内部使用计数器不被重置。

运行所有计时器#

我们可能想为这个模块编写的另一个测试是断言回调函数在1秒后被调用。为了做到这一点,我们将在测试中间使用Jest的计时器控制api来快进时间:

测试 ( '在1秒后调用回调' , ( ) = > {
常量 timerGame = 需要 ( “. . / timerGame” ) ;
常量 回调 = 开玩笑 fn ( ) ;
timerGame ( 回调 ) ;
//此时,回调函数还没有被调用
预计 ( 回调 ) toBeCalled ( ) ;
//快进直到所有计时器都被执行
开玩笑 runAllTimers ( ) ;
//现在我们的回调应该被调用了!
预计 ( 回调 ) toBeCalled ( ) ;
预计 ( 回调 ) toHaveBeenCalledTimes ( 1 ) ;
} ) ;

等待运行计时器#

还有一种情况是,您可能有一个递归计时器——这是一个计时器,它在自己的回调中设置一个新的计时器。对于这些游戏来说,运行所有计时器将是一个无尽的循环jest.runAllTimers ()是不可取的。对于这些情况,您可能会使用jest.runOnlyPendingTimers ():

/ / infiniteTimerGame.js
使用严格的 ;
函数 infiniteTimerGame ( 回调 ) {
控制台 日志 ( “准备好了……走吧!” ) ;
setTimeout ( ( ) = > {
控制台 日志 ( “时间到了!下一场比赛开始前10秒……” ) ;
回调 & & 回调 ( ) ;
// 10秒后安排下一场比赛
setTimeout ( ( ) = > {
infiniteTimerGame ( 回调 ) ;
} , 10000 ) ;
} , 1000 ) ;
}
模块 出口 = infiniteTimerGame ;
/ / __tests__ / infiniteTimerGame-test.js
使用严格的 ;
开玩笑 useFakeTimers ( ) ;
描述 ( “infiniteTimerGame” , ( ) = > {
测试 ( “在1秒之后安排10秒的计时器” , ( ) = > {
常量 infiniteTimerGame = 需要 ( “. . / infiniteTimerGame” ) ;
常量 回调 = 开玩笑 fn ( ) ;
infiniteTimerGame ( 回调 ) ;
//在这个时间点上,应该有一个单一的调用
// setTimeout设置游戏在1秒内结束。
预计 ( setTimeout ) toHaveBeenCalledTimes ( 1 ) ;
预计 ( setTimeout ) toHaveBeenLastCalledWith ( 预计 任何 ( 函数 ) , 1000 ) ;
//快进和耗尽当前挂起的计时器
//(但不是在进程中创建的任何新计时器)
开玩笑 runOnlyPendingTimers ( ) ;
//此时,我们的1秒计时器应该触发它的回调函数
预计 ( 回调 ) toBeCalled ( ) ;
//它应该创建了一个新的计时器来重新开始游戏
/ / 10秒
预计 ( setTimeout ) toHaveBeenCalledTimes ( 2 ) ;
预计 ( setTimeout ) toHaveBeenLastCalledWith ( 预计 任何 ( 函数 ) , 10000 ) ;
} ) ;
} ) ;

按时间提前计时器#

重命名的runTimersToTimeadvanceTimersByTime在开玩笑22.0.0#

另一种可能性是使用jest.advanceTimersByTime (msToRun)。当调用这个API时,所有计时器都被提前msToRun毫秒。所有通过setTimeout()或setInterval()排队的未决“宏任务”,并将在此时间段内执行,都将被执行。此外,如果这些宏任务计划在相同的时间框架内执行新的宏任务,那么这些宏任务将一直执行,直到队列中没有更多应该在msToRun毫秒内运行的宏任务为止。

/ / timerGame.js
使用严格的 ;
函数 timerGame ( 回调 ) {
控制台 日志 ( “准备好了……走吧!” ) ;
setTimeout ( ( ) = > {
控制台 日志 ( “时间到了——停!” ) ;
回调 & & 回调 ( ) ;
} , 1000 ) ;
}
模块 出口 = timerGame ;
( '通过advanceTimersByTime在1秒后调用回调' , ( ) = > {
常量 timerGame = 需要 ( “. . / timerGame” ) ;
常量 回调 = 开玩笑 fn ( ) ;
timerGame ( 回调 ) ;
//此时,回调函数还没有被调用
预计 ( 回调 ) toBeCalled ( ) ;
//快进直到所有计时器都被执行
开玩笑 advanceTimersByTime ( 1000 ) ;
//现在我们的回调应该被调用了!
预计 ( 回调 ) toBeCalled ( ) ;
预计 ( 回调 ) toHaveBeenCalledTimes ( 1 ) ;
} ) ;

最后,在某些测试中,清除所有挂起的计时器偶尔会很有用。为此,我们有jest.clearAllTimers ()

这个例子的代码可以在下面找到例子/计时器

最后一次更新在通过Sebastien Lorber