В тестировании компонентов Vue часто возникают ситуации, когда нужно
корректно обрабатывать асинхронные операции —
обновление данных, вызовы API, таймеры и переходы состояний. Для этого в
экосистеме Vue Test Utils применяются два ключевых инструмента:
flushPromises и таймеры Jest
(jest.useFakeTimers). Их использование позволяет создавать
стабильные и предсказуемые тесты для асинхронного кода.
flushPromises — это утилита, которая позволяет дождаться
выполнения всех ожидающих промисов в текущем цикле событий. В Vue это
особенно важно из-за реактивной природы данных: изменения состояния
могут инициировать обновление DOM только после завершения всех
промисов.
import { mount } from '@vue/test-utils';
import flushPromises from 'flush-promises';
import MyComponent from '@/components/MyComponent.vue';
test('обновление данных после промиса', async () => {
const wrapper = mount(MyComponent);
wrapper.vm.fetchData(); // метод, который возвращает промис
await flushPromises(); // ждём завершения всех промисов
expect(wrapper.text()).toContain('Данные загружены');
});
Особенности использования
flushPromises:
then.setTimeout, нужно обрабатывать отдельно.Многие компоненты используют setTimeout,
setInterval или requestAnimationFrame для
отложенного обновления состояния. Для тестирования таких случаев
применяется искусственное управление временем через
jest.useFakeTimers.
jest.useFakeTimers();
test('ожидание setTimeout', () => {
const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000); // перемещаем виртуальные часы на 1 секунду
expect(callback).toHaveBeenCalled();
});
Ключевые моменты при работе с fake timers в Vue:
flushPromises: если внутри
таймера вызывается асинхронный код, необходимо сначала продвинуть
таймеры, а затем выполнить flushPromises.jest.runAllTimers() vs
jest.advanceTimersByTime(ms): первый метод
выполняет все запланированные таймеры сразу, второй позволяет
контролировать временной промежуток.await wrapper.vm.$nextTick(), чтобы Vue успел применить
реактивные изменения к DOM.test('асинхронное обновление с таймером', async () => {
const wrapper = mount(MyComponent);
wrapper.vm.startTimer(); // метод с setTimeout и асинхронной операцией
jest.advanceTimersByTime(500); // промежуточное время
expect(wrapper.text()).not.toContain('Готово');
jest.advanceTimersByTime(500); // таймер завершен
await flushPromises(); // ждём завершения промиса
await wrapper.vm.$nextTick(); // ждём обновления DOM
expect(wrapper.text()).toContain('Готово');
});
flushPromises и fake timersНа практике часто встречаются сценарии, где компонент одновременно использует асинхронные промисы и таймеры. Для корректного тестирования важно соблюдать последовательность действий:
jest.useFakeTimers()).jest.advanceTimersByTime(ms) или
jest.runAllTimers().await flushPromises() для завершения всех
промисов.await wrapper.vm.$nextTick().Эта последовательность гарантирует, что все асинхронные операции полностью завершены перед проверкой состояния компонента.
await flushPromises() после
промисов, даже если таймер уже завершился, иначе изменения
состояния могут быть не применены.$nextTick) для актуального DOM.Для полноценного использования этих техник в проекте необходимы:
@vue/test-utils — основной инструмент для рендеринга и
взаимодействия с компонентами.flush-promises — библиотека для ожидания всех
промисов.jest — тестовый рантайм с поддержкой fake timers и
моков.Настройка fake timers выполняется на уровне теста или в
beforeEach/beforeAll:
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.runOnlyPendingTimers();
jest.useRealTimers();
});
Такой подход обеспечивает чистое состояние между тестами и предотвращает утечки таймеров.
Использование комбинации flushPromises и fake timers
обеспечивает полный контроль над асинхронными процессами и
таймерами в тестах Vue-компонентов, делая тесты надежными,
предсказуемыми и независимыми от внешних задержек.