flushPromises() — это утилита, используемая в
тестировании компонентов Vue для управления асинхронными процессами. Она
позволяет дождаться завершения всех промисов в очереди микротасков, что
особенно важно при тестировании компонентов, использующих асинхронные
вызовы, такие как API-запросы, nextTick, таймеры с
Promise или асинхронные вычисляемые свойства.
JavaScript обрабатывает асинхронный код через
микротаски и макротаски. Промисы
помещаются в очередь микротасков, которая выполняется после текущего
стека вызовов, но до следующей макротаски. В тестах Vue это может
приводить к ситуации, когда компоненты еще не обновили состояние после
асинхронных операций, а утверждения (assert) выполняются
сразу. flushPromises() решает эту проблему, принудительно
ожидая завершения всех промисов перед проверкой состояния
компонента.
Простейшая реализация flushPromises() выглядит так:
export function flushPromises() {
return new Promise(resolve => setTimeout(resolve, 0));
}
Эта конструкция позволяет отложить выполнение до следующей макротаски, тем самым гарантируя завершение всех текущих микротасков с промисами.
flushPromises() обычно применяется вместе с функцией
mount или shallowMount из
@vue/test-utils для тестирования компонентов с асинхронными
эффектами. Рассмотрим пример:
import { mount } from '@vue/test-utils';
import flushPromises from './flushPromises';
import MyComponent from '@/components/MyComponent.vue';
import axios from 'axios';
jest.mock('axios');
test('отрисовка данных после API-запроса', async () => {
axios.get.mockResolvedValue({ data: { message: 'Привет' } });
const wrapper = mount(MyComponent);
// Компонент инициирует API-запрос в mounted()
await flushPromises();
expect(wrapper.text()).toContain('Привет');
});
В этом примере вызов flushPromises() гарантирует, что
все промисы, созданные в mounted(), будут завершены до
проверки содержимого компонента.
flushPromises() полезна при тестировании методов,
возвращающих промисы, и событий, которые инициируют асинхронные
процессы.
test('обновление состояния после асинхронного метода', async () => {
const wrapper = mount(MyComponent);
wrapper.vm.loadData(); // метод возвращает промис
await flushPromises();
expect(wrapper.vm.dataLoaded).toBe(true);
});
Здесь flushPromises() обеспечивает синхронизацию теста с
внутренним состоянием компонента, обновленным после завершения
промиса.
nextTickVue использует реактивность через механизм реактивных эффектов и
очередь обновлений. Иногда после асинхронного промиса нужно дождаться
обновления DOM. В таких случаях flushPromises() комбинируют
с nextTick():
import { nextTick } from 'vue';
await flushPromises();
await nextTick();
expect(wrapper.find('p').text()).toBe('Данные загружены');
flushPromises() дожидается завершения всех
промисов.nextTick() гарантирует, что Vue обновил DOM на основе
изменений реактивного состояния.flushPromises() также позволяет корректно тестировать
обработку ошибок:
axios.get.mockRejectedValue(new Error('Ошибка загрузки'));
const wrapper = mount(MyComponent);
await flushPromises();
expect(wrapper.text()).toContain('Ошибка загрузки');
Без flushPromises() промис, выбросивший ошибку, может
остаться незавершенным, и тест не сможет проверить отображение сообщения
об ошибке.
flushPromises() не гарантирует завершение промисов, которые
создаются внутри промисов после первого вызова. В таких случаях может
потребоваться несколько последовательных вызовов
flushPromises().flushPromises() рекомендуется использовать
nextTick() для проверки DOM.setTimeout или setInterval, их необходимо
контролировать через Jest таймеры или библиотеку
vi.useFakeTimers().axios,
fetch)flushPromises() является стандартным инструментом для
обеспечения корректного и надежного тестирования асинхронного поведения
компонентов Vue, предотвращая ошибки, связанные с преждевременной
проверкой состояния или DOM.