Jest timer для контроля анимаций

В процессе тестирования веб-приложений часто возникает необходимость в контроле времени. Особенно это важно, когда речь идет о компонентах, включающих анимации или асинхронные операции, зависящие от временных задержек. Jest, популярный фреймворк для тестирования JavaScript, предоставляет встроенные инструменты для управления таймерами, которые позволяют точно контролировать время в тестах и избегать реальных ожиданий, что значительно ускоряет процесс тестирования.

Зачем использовать таймеры в тестах?

Многие компоненты, построенные с использованием библиотек анимаций (например, react-spring или framer-motion), используют таймеры для задания продолжительности анимации или асинхронных переходов. В тестах важно точно управлять временем, чтобы контролировать последовательность событий, а также избегать «случайных» задержек, которые могут повлиять на стабильность тестов.

Без использования таймеров в Jest, тесты, основанные на анимациях, могут быть ненадежными из-за реальных временных задержек. Это может приводить к флуктуациям в результатах тестов или даже к их неудаче, если анимация не успевает завершиться до завершения теста.

Как работают таймеры в Jest?

Jest предоставляет несколько утилит для работы с таймерами:

  • jest.useFakeTimers() — переключает Jest в режим использования поддельных (mock) таймеров. Это позволяет тестировать асинхронные операции, не дожидаясь реального времени.
  • jest.runAllTimers() — выполняет все таймеры, которые были запланированы с момента вызова.
  • jest.advanceTimersByTime(ms) — сдвигает время на указанное количество миллисекунд, позволяя имитировать прохождение времени.
  • jest.clearAllTimers() — очищает все активные таймеры.

Подготовка теста с таймерами

Для начала необходимо использовать функцию jest.useFakeTimers(). Это позволяет Jest “подменить” стандартные таймеры браузера на свои собственные mock-версии, которые можно контролировать в рамках теста. Таймеры работают аналогично реальным, но их можно ускорять, замедлять или контролировать вручную.

Пример настройки:

beforeAll(() => {
  jest.useFakeTimers();
});

afterAll(() => {
  jest.useRealTimers();
});

После этого можно манипулировать временем с помощью других методов, таких как jest.advanceTimersByTime(). Важно помнить, что флаг useFakeTimers() должен быть установлен до выполнения тестов, чтобы тесты корректно использовали поддельные таймеры.

Тестирование анимаций с использованием jest.advanceTimersByTime

В случае с анимациями, важно не только переключить Jest на поддельные таймеры, но и корректно имитировать прохождение времени, чтобы убедиться в правильности работы компонента. Например, если анимация длится 300 миллисекунд, можно использовать jest.advanceTimersByTime(300) для симуляции завершения анимации.

Пример теста компонента с анимацией:

import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';

test('анимируется при нажатии кнопки', () => {
  render(<MyComponent />);
  
  const button = screen.getByText('Начать анимацию');
  fireEvent.click(button);
  
  // Время анимации: 300 мс
  jest.advanceTimersByTime(300);

  // Проверка, что элемент изменил стиль после анимации
  const animatedElement = screen.getByTestId('animated-element');
  expect(animatedElement).toHaveStyle('transform: translateX(100px)');
});

В данном примере, после клика по кнопке начинается анимация, которая длится 300 миллисекунд. С помощью jest.advanceTimersByTime(300) время продвигается на 300 миллисекунд, что позволяет сразу проверить результат анимации.

Использование jest.runAllTimers()

Если требуется выполнить все запланированные таймеры, можно использовать метод jest.runAllTimers(). Это полезно в тестах, где несколько таймеров могут быть запланированы одновременно, и нужно немедленно выполнить их все.

Пример с несколькими таймерами:

import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';

test('несколько таймеров выполняются при событии', () => {
  render(<MyComponent />);
  
  const button = screen.getByText('Запустить анимацию');
  fireEvent.click(button);
  
  // Запуск нескольких таймеров
  jest.runAllTimers();
  
  // Проверка того, что все таймеры были выполнены
  const element = screen.getByTestId('animated-element');
  expect(element).toHaveClass('finished');
});

Здесь при нажатии на кнопку запускаются несколько анимаций, и jest.runAllTimers() позволяет немедленно продвинуть все таймеры, что упрощает тестирование нескольких асинхронных операций, которые зависят от времени.

Управление временем с jest.clearAllTimers()

После выполнения тестов, когда больше не требуется контролировать таймеры, можно очистить все активные таймеры с помощью метода jest.clearAllTimers(). Это помогает предотвратить утечки и потенциальные проблемы с таймерами, которые могут повлиять на другие тесты.

afterEach(() => {
  jest.clearAllTimers();
});

Это полезно, если в тестах используются таймеры, и важно убедиться, что состояние таймеров не переносится в другие тесты.

Пример с асинхронными анимациями

Если в компоненте используются асинхронные операции, например, задержка с использованием setTimeout, можно комбинировать jest.useFakeTimers() с асинхронными ожиданиями.

test('асинхронная анимация с задержкой', async () => {
  render(<MyComponent />);
  
  const button = screen.getByText('Запустить анимацию');
  fireEvent.click(button);
  
  // Установка таймеров
  jest.advanceTimersByTime(500); // Имитация времени задержки
  
  const element = await screen.findByTestId('animated-element');
  expect(element).toHaveStyle('opacity: 1');
});

Здесь jest.advanceTimersByTime() используется для имитации ожидания, которое происходит во время асинхронной операции. Это позволяет корректно тестировать компоненты с задержками.

Заключение

Использование Jest таймеров для контроля анимаций в тестах — это важный инструмент для упрощения и ускорения тестирования, когда работа компонента зависит от времени. Контроль времени через jest.useFakeTimers(), jest.advanceTimersByTime(), и другие методы Jest позволяет эффективно управлять асинхронными операциями и анимациями, минимизируя зависимость от реального времени и обеспечивая стабильность тестов.