【Jest_47】 setTimeoutやsetIntervalをテストするには?
JestでsetTimeout/setIntervalをテストする
1. はじめに
setTimeoutやsetIntervalはJavaScriptの基本的な非同期タイマー関数ですが、Jestでテストするときは、
実際に待機時間を使うとテストが遅くなる
タイマーの挙動を正確に検証したい
などの理由からタイマーをモック(偽装)して制御するのがベストプラクティスです。
2. Jestのタイマーモックの基本
Jestはタイマー関数をモック化でき、擬似的に時間を進められます。
jest.useFakeTimers();これでsetTimeoutやsetIntervalがモック化される
タイマーのコールバックは手動で時間を進めない限り実行されない
3. 基本的なセットアップ
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});beforeEachでタイマーをモック化
afterEachで元に戻すのが推奨
4. setTimeoutのテスト例
4-1. テスト対象関数
function delayedCallback(callback) {
setTimeout(() => {
callback('done');
}, 1000);
}4-2. テストコード
test('delayedCallback calls callback after 1 second', () => {
const mockCallback = jest.fn();
delayedCallback(mockCallback);
// タイマーはまだ実行されていない
expect(mockCallback).not.toBeCalled();
// タイマーを進める
jest.advanceTimersByTime(1000);
// コールバックが呼ばれたか検証
expect(mockCallback).toBeCalledWith('done');
});jest.advanceTimersByTime(ms) で指定ミリ秒分だけタイマーを進める
コールバックが呼ばれたかを検証
5. setIntervalのテスト例
5-1. テスト対象関数
function repeatCallback(callback) {
let count = 0;
const intervalId = setInterval(() => {
count++;
callback(count);
if (count >= 3) clearInterval(intervalId);
}, 500);
}5-2. テストコード
test('repeatCallback calls callback 3 times', () => {
const mockCallback = jest.fn();
repeatCallback(mockCallback);
expect(mockCallback).not.toBeCalled();
// 500msごとにタイマーを進める
jest.advanceTimersByTime(500);
expect(mockCallback).toHaveBeenLastCalledWith(1);
jest.advanceTimersByTime(500);
expect(mockCallback).toHaveBeenLastCalledWith(2);
jest.advanceTimersByTime(500);
expect(mockCallback).toHaveBeenLastCalledWith(3);
// もう呼ばれないことも検証できる
jest.advanceTimersByTime(500);
expect(mockCallback).toHaveBeenCalledTimes(3);
});6. jest.runAllTimers()やjest.runOnlyPendingTimers()の活用
jest.runAllTimers(): 保留中のすべてのタイマーを即時実行
jest.runOnlyPendingTimers(): 現在保留中のタイマーのみ実行
test('runAllTimers example', () => {
const mockCallback = jest.fn();
delayedCallback(mockCallback);
jest.runAllTimers();
expect(mockCallback).toBeCalled();
});7. async/awaitと組み合わせる場合のポイント
7-1. タイマーのモック中にPromiseを待つ例
test('async with timers', async () => {
const mockCallback = jest.fn();
delayedCallback(mockCallback);
// タイマーを進めてコールバックを呼ぶ
jest.advanceTimersByTime(1000);
// Promiseの処理がある場合は待機
await Promise.resolve();
expect(mockCallback).toBeCalled();
});8. 実環境のタイマーを使うテストに戻す
jest.useRealTimers();テストによっては実タイマーを使いたい場合もある
useRealTimers() で戻せる
9. 注意点・よくある間違い
モックタイマーを使わずに待機する
テストが遅くなる、動作が不安定になる
タイマーを進めずにアサーションする
コールバックが呼ばれていないのでテストが失敗する
async関数との組み合わせでresolve待ちを忘れる
非同期処理の完了がテストに反映されず誤検知の原因になる
10. まとめ
jest.useFakeTimers()
タイマー関数をモック化し時間を制御できるjest.advanceTimersByTime(ms)
指定時間だけタイマーを進める
jest.runAllTimers()
保留中の全タイマーを即時実行
jest.useRealTimers()
実際のタイマーに戻す
async/awaitとの併用
await Promise.resolve()でイベントループ待ち合わせ
