Тестирование асинхронных операций является важной частью любого
процесса тестирования, и функции setTimeout и
setInterval представляют собой часто используемые
инструменты для выполнения отложенных и повторяющихся действий. Однако
при написании юнит-тестов для кода, использующего эти функции,
необходимо учитывать их асинхронную природу. Jest предоставляет мощные
инструменты для эффективного тестирования этих операций, позволяя
симулировать и контролировать время, избегая реальных задержек.
setTimeout и setInterval — это стандартные
функции JavaScript, которые позволяют управлять временем выполнения
кода:
Обе функции возвращают уникальные идентификаторы таймеров, которые
могут быть использованы для их отмены через clearTimeout и
clearInterval соответственно.
При использовании setTimeout и setInterval
тестирование может столкнуться с рядом проблем. В частности:
Jest решает эти проблемы, предоставляя средства для управления временем, такие как функции для контроля времени и моки для таймеров.
Jest предоставляет два важных метода для работы с таймерами:
Для начала, необходимо активировать фейковые таймеры с помощью
jest.useFakeTimers():
jest.useFakeTimers();
После этого любые вызовы setTimeout и
setInterval будут контролироваться Jest, и можно будет
манипулировать временем, не затрачивая фактическое время ожидания.
Для того чтобы протестировать код, использующий
setTimeout, необходимо:
jest.useFakeTimers().setTimeout.jest.advanceTimersByTime(ms) или
jest.runAllTimers().Пример:
test('setTimeout вызывает колбэк после задержки', () => {
jest.useFakeTimers();
const mockCallback = jest.fn();
setTimeout(mockCallback, 1000);
// Убедимся, что колбэк не вызван сразу
expect(mockCallback).not.toHaveBeenCalled();
// Симулируем прохождение времени
jest.advanceTimersByTime(1000);
// Проверяем, что колбэк был вызван
expect(mockCallback).toHaveBeenCalled();
});
В этом примере:
jest.useFakeTimers().jest.fn().setTimeout используем
jest.advanceTimersByTime(1000), чтобы симулировать
прохождение времени на 1000 мс.Для тестирования функции, использующей setInterval,
принцип остаётся схожим. В отличие от setTimeout, здесь
важно учитывать, что интервал будет повторяться до тех пор, пока не
будет отменён. Для этого можно использовать
jest.runAllTimers(), чтобы сразу выполнить все интервальные
таймеры.
Пример:
test('setInterval вызывает колбэк повторно через интервал', () => {
jest.useFakeTimers();
const mockCallback = jest.fn();
setInterval(mockCallback, 1000);
// Симулируем прохождение времени
jest.advanceTimersByTime(1000);
// Проверяем, что колбэк был вызван 1 раз
expect(mockCallback).toHaveBeenCalledTimes(1);
// Симулируем ещё 1000 мс
jest.advanceTimersByTime(1000);
// Проверяем, что колбэк был вызван 2 раза
expect(mockCallback).toHaveBeenCalledTimes(2);
});
Здесь:
setInterval, который должен вызывать
mockCallback каждые 1000 мс.jest.advanceTimersByTime(1000)
проверяем, сколько раз был вызван колбэк.setTimeout,
setInterval будет вызываться многократно, пока не будет
отменён.При тестировании важно учитывать возможность отмены таймеров,
особенно для setInterval, чтобы предотвратить бесконечные
циклы. Для этого можно использовать clearTimeout и
clearInterval.
Пример:
test('clearInterval останавливает повторяющийся вызов', () => {
jest.useFakeTimers();
const mockCallback = jest.fn();
const intervalId = setInterval(mockCallback, 1000);
// Симулируем прохождение времени
jest.advanceTimersByTime(3000);
// Проверяем, что колбэк был вызван 3 раза
expect(mockCallback).toHaveBeenCalledTimes(3);
// Останавливаем интервал
clearInterval(intervalId);
// Симулируем ещё 1000 мс
jest.advanceTimersByTime(1000);
// Проверяем, что колбэк больше не был вызван
expect(mockCallback).toHaveBeenCalledTimes(3);
});
Здесь:
clearInterval, чтобы остановить дальнейшие вызовы.Иногда нужно сразу выполнить все таймеры, не симулируя их по
отдельности. В этом случае можно использовать
jest.runAllTimers(). Этот метод выполняет все ожидающие
таймеры, что полезно, когда нужно проверить несколько операций с
таймерами.
Пример:
test('jest.runAllTimers выполняет все таймеры', () => {
jest.useFakeTimers();
const mockCallback = jest.fn();
setTimeout(mockCallback, 1000);
setTimeout(mockCallback, 2000);
// Проверяем, что колбэк ещё не был вызван
expect(mockCallback).not.toHaveBeenCalled();
// Выполняем все таймеры
jest.runAllTimers();
// Проверяем, что колбэк был вызван 2 раза
expect(mockCallback).toHaveBeenCalledTimes(2);
});
В этом примере:
jest.runAllTimers() для немедленного
выполнения всех ожидающих таймеров.Тестирование функций, использующих setTimeout и
setInterval, может быть сложным, если подходить к этому без
использования Jest. Включение фейковых таймеров и управление временем с
помощью таких методов, как jest.advanceTimersByTime() и
jest.runAllTimers(), позволяет эффективно тестировать
асинхронные операции без ожидания реальных задержек. Это значительно
ускоряет выполнение тестов и улучшает их предсказуемость.