Фейковые таймеры
Урок учит тестировать код с setTimeout/setInterval без реального ожидания времени.
Фейковые таймеры подменяют setTimeout/setInterval, давая тесту управлять «временем» вручную.
Проблема настоящего времени
Код с setTimeout(fn, 5000) нельзя честно тестировать, ожидая 5 секунд: тесты должны быть быстрыми. И от реального времени они становятся недетерминированными. Решение — фейковые таймеры: время «перематывает» сам тест.
Включение фейковых таймеров
jest.useFakeTimers();
test('колбэк вызывается через 1 секунду', () => {
const cb = jest.fn();
setTimeout(cb, 1000);
expect(cb).not.toHaveBeenCalled(); // время ещё не прошло
jest.advanceTimersByTime(1000); // перематываем 1 секунду
expect(cb).toHaveBeenCalledTimes(1); // теперь вызвался
});Тест выполняется мгновенно, хотя «логически» прошла секунда.
Полезные методы
| Метод | Что делает |
advanceTimersByTime(ms) | перематывает время на ms миллисекунд |
runAllTimers() | выполняет все ожидающие таймеры |
runOnlyPendingTimers() | выполняет только текущие (без новых, созданных ими) |
useRealTimers() | возвращает настоящие таймеры |
Идея «виртуального времени» на чистом JS
Смоделируем перемотку времени без реального ожидания:
// мини-планировщик с виртуальными часами
const queue = [];
let now = 0;
function schedule(fn, delay) {
queue.push({ time: now + delay, fn });
}
function advance(ms) {
now += ms;
queue.filter(t => t.time <= now).forEach(t => t.fn());
}
let called = false;
schedule(() => { called = true; }, 1000);
console.log('сразу после schedule, called =', called);
advance(1000); // "перематываем" 1 секунду
console.log('после advance(1000), called =', called);Вывод:
сразу после schedule, called = false после advance(1000), called = true
Именно так и работают фейковые таймеры Jest: задачи лежат в очереди с «временем срабатывания», а advanceTimersByTime двигает виртуальные часы и выполняет всё, что подошло.
Не забывайте откатить
После теста с фейковыми таймерами их сбрасывают, чтобы не сломать соседние тесты:
afterEach(() => {
jest.useRealTimers();
});Итог
jest.useFakeTimers()подменяет таймеры, чтобы не ждать реальное время.advanceTimersByTime(ms)перематывает время и выполняет наступившие колбэки.- Тест становится быстрым и детерминированным.
- После —
useRealTimers()для изоляции.
Проверьте себя
1. Зачем нужны фейковые таймеры?
AЧтобы тесты выполнялись медленнее
BЧтобы не ждать реальное время и сделать тесты быстрыми и детерминированными
CЧтобы заменить expect
DЧтобы ускорить сеть
2. Что делает jest.advanceTimersByTime(1000)?
AЖдёт 1000 реальных миллисекунд
BПерематывает виртуальное время на 1000 мс и выполняет наступившие таймеры
CУдаляет все таймеры
DЗамедляет таймеры в 1000 раз
3. Почему после теста с фейковыми таймерами вызывают useRealTimers()?
AДля скорости установки
BЧтобы вернуть настоящие таймеры и не сломать другие тесты
CЧтобы удалить моки
DЭто обязательное требование npm