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

タイマーモック

ネイティブのタイマー関数(例如,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やその他のタイマー関数をモック関数でモックします。1つのファイル中や描述ブロック中のテストを複数実行する場合,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 ) ;
} ) ;
} ) ;

指定した時間でタイマーを進める#

开玩笑22.0.0runTimersToTimeからadvanceTimersByTimeに改名#

別の可能性としてはjest.advanceTimersByTime (msToRun)を使うことです。このAPIが呼び出されると,すべてのタイマーはmsToRunミリ秒で進みます。このAPIが呼び出されると,すべてのタイマーはmsToRunミリ秒で進みます。setTimeout()またはsetInterval()経由でキューイングされ保留中であった,その時間内に実行予定の“宏观任务”が実行されます。

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

最後に,保留中のすべてのタイマーをクリアすることはテストによっては役立つことがあります。そのためにJestにはjest.clearAllTimers ()があります。

この例のコードは例子/计时器で参照できます。