Таймеры — важная часть асинхронного программирования в JavaScript. В
процессе тестирования часто возникает необходимость замедлить выполнение
тестов, подменить поведение таймеров или изменить их взаимодействие с
кодом. Jest предоставляет ряд инструментов для эффективного мокирования
таймеров, таких как setTimeout, setInterval, а
также функции, связанные с их отменой — clearTimeout,
clearInterval. Мокирование этих функций позволяет
тестировать асинхронный код, не затрачивая время на фактические
задержки.
Jest предлагает встроенные методы для работы с таймерами:
jest.useFakeTimers() — переключает
Jest в режим использования фальшивых таймеров.jest.runAllTimers() — выполняет все
отложенные таймеры, включая все функции, ожидающие выполнения через
setTimeout или setInterval.jest.runOnlyPendingTimers() —
выполняет только те таймеры, которые находятся в очереди на выполнение,
без выполнения тех, которые были запланированы позже.jest.advanceTimersByTime(ms) —
продвигает время на указанное количество миллисекунд, что позволяет
“прокачать” таймеры вперед.Когда используется
jest.useFakeTimers(), Jest подменяет
стандартные таймеры на свои фальшивые аналоги. Это позволяет вам
контролировать, когда и как должны срабатывать таймеры, и использовать
такие методы, как runAllTimers для эмуляции времени без
ожидания реальных задержек. Включить фальшивые таймеры можно следующим
образом:
jest.useFakeTimers();
Этот метод также позволяет мокировать поведение таймеров для тестов с асинхронным кодом, который использует таймеры для задержки выполнения.
setTimeout и setIntervalКогда тестируемый код использует функции setTimeout или
setInterval, вы можете управлять их выполнением с помощью
фальшивых таймеров. Например:
test('mocking setTimeout with fake timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
// Таймер ещё не сработал
expect(callback).not.toHaveBeenCalled();
// Прокачиваем время
jest.advanceTimersByTime(1000);
// Таймер сработал
expect(callback).toHaveBeenCalled();
});
В этом примере таймер setTimeout срабатывает только
после того, как мы вручную продвигаем время на 1000 миллисекунд. Это
позволяет тестировать код, зависящий от времени, без фактической
задержки.
clearTimeout и clearIntervalВместе с мокированием таймеров часто используется отмена их
выполнения. Функции clearTimeout и
clearInterval обеспечивают очистку таймеров, предотвращая
их выполнение. Мокирование этих функций также возможно с помощью
Jest.
Пример использования clearTimeout в тестах:
test('clearTimeout works with fake timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
const timerId = setTimeout(callback, 1000);
// Останавливаем таймер до того, как он сработает
clearTimeout(timerId);
// Прокачиваем время
jest.advanceTimersByTime(1000);
// Таймер не должен был сработать
expect(callback).not.toHaveBeenCalled();
});
В реальной разработке часто встречаются ситуации, когда асинхронные операции, такие как таймеры, выполняются внутри функций. В таких случаях мокирование таймеров позволяет контролировать последовательность выполнения этих операций.
Пример:
test('async function with timers', () => {
jest.useFakeTimers();
const asyncFunction = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('done');
}, 1000);
});
};
// Запускаем асинхронную функцию
const promise = asyncFunction();
// Таймер ещё не сработал, промис ещё не завершён
expect(promise).not.toBeResolved();
// Прокачиваем время
jest.advanceTimersByTime(1000);
// Промис должен быть завершён
return expect(promise).resolves.toBe('done');
});
В данном примере асинхронная функция использует
setTimeout, и с помощью
jest.advanceTimersByTime мы можем контролировать завершение
промиса, не дожидаясь реальной задержки.
Когда в тестах используются фальшивые таймеры, важно вернуть всё к
нормальному состоянию после их использования. Для этого Jest
предоставляет метод jest.useRealTimers(),
который восстанавливает оригинальные таймеры.
test('mocking setTimeout and restoring real timers', () => {
jest.useFakeTimers();
const callback = jest.fn();
setTimeout(callback, 1000);
jest.runAllTimers();
expect(callback).toHaveBeenCalled();
// Восстанавливаем оригинальные таймеры
jest.useRealTimers();
});
Это позволяет избежать влияния мокированных таймеров на другие тесты.
jest.useFakeTimers() влияет только на текущий
тестовый блок, и не затрагивает глобальную настройку для всех
тестов в файле.jest.advanceTimersByTime() следует
помнить, что все таймеры, которые должны были сработать за это время,
будут выполнены сразу, что может повлиять на логику тестов.Для более сложных операций, где таймеры используются в связке с другими асинхронными задачами (например, в сети или при чтении/записи данных), мокирование таймеров в Jest позволяет легко тестировать сценарии с задержками.
test('async operation with setTimeout', () => {
jest.useFakeTimers();
const fetchData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('data');
}, 2000);
});
};
const dataPromise = fetchData();
// Прокачиваем время на 2000 миллисекунд
jest.advanceTimersByTime(2000);
return expect(dataPromise).resolves.toBe('data');
});
В данном примере мокирование таймеров позволяет нам не ждать фактической задержки в 2 секунды, а сразу проверить результат.
Мокирование таймеров в Jest — мощный инструмент для управления
временем в тестах, позволяя ускорить их выполнение и избежать
зависимости от реальных временных задержек. Это особенно полезно для
тестирования асинхронных операций, которые используют
setTimeout и setInterval. Умение правильно
работать с мокированными таймерами делает тесты более контролируемыми и
быстрыми, что способствует повышению качества и скорости разработки.