В процессе тестирования веб-приложений часто возникает необходимость в контроле времени. Особенно это важно, когда речь идет о компонентах, включающих анимации или асинхронные операции, зависящие от временных задержек. Jest, популярный фреймворк для тестирования JavaScript, предоставляет встроенные инструменты для управления таймерами, которые позволяют точно контролировать время в тестах и избегать реальных ожиданий, что значительно ускоряет процесс тестирования.
Многие компоненты, построенные с использованием библиотек анимаций
(например, react-spring или framer-motion),
используют таймеры для задания продолжительности анимации или
асинхронных переходов. В тестах важно точно управлять временем, чтобы
контролировать последовательность событий, а также избегать «случайных»
задержек, которые могут повлиять на стабильность тестов.
Без использования таймеров в 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 позволяет
эффективно управлять асинхронными операциями и анимациями, минимизируя
зависимость от реального времени и обеспечивая стабильность тестов.