Ожидание изменений (await wrapper.vm.$nextTick)

При тестировании компонентов Vue часто возникает необходимость убедиться, что изменения в состоянии компонента или его реактивных данных полностью отразились на DOM. Vue использует асинхронный механизм обновления, что означает, что изменения в реактивных данных не мгновенно отображаются в шаблоне. Для правильного тестирования необходимо дождаться завершения этих асинхронных обновлений.

Механизм $nextTick

Метод $nextTick является встроенным в экземпляр Vue и возвращает промис, который разрешается после того, как все реактивные обновления DOM будут завершены. Это позволяет безопасно проверять состояние компонента и его разметку после изменений данных.

Пример базового использования:

import { mount } from '@vue/test-utils';
import Counter from '@/components/Counter.vue';

test('увеличение счётчика', async () => {
  const wrapper = mount(Counter);

  wrapper.vm.count++;
  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('1');
});

В этом примере увеличение значения count не сразу отражается в DOM. Без await wrapper.vm.$nextTick() проверка expect(wrapper.text()).toContain('1') может завершиться неудачей, так как обновление DOM ещё не произошло.

Когда использовать $nextTick

  1. Изменение реактивных данных: любые изменения в data или computed, которые влияют на шаблон.
  2. Вызов методов компонента: если метод модифицирует состояние, которое должно быть проверено в DOM.
  3. Асинхронные события: например, после обработки события @click или @input.

Пример с событием:

test('обработка клика кнопки', async () => {
  const wrapper = mount(Counter);
  
  await wrapper.find('button').trigger('click');
  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('1');
});

trigger автоматически вызывает обработчик события, который изменяет count. Чтобы убедиться, что DOM обновился, необходимо дождаться nextTick.

Комбинация с асинхронными функциями

Если метод компонента возвращает промис или использует setTimeout внутри, await wrapper.vm.$nextTick() гарантирует, что все микротаски Vue будут выполнены. Для асинхронных действий, которые зависят от промисов, часто используют комбинацию с await flushPromises() (внешняя утилита для ожидания всех промисов) перед $nextTick.

Пример:

import flushPromises from 'flush-promises';

test('асинхронное обновление данных', async () => {
  const wrapper = mount(AsyncComponent);

  wrapper.vm.loadData(); // метод возвращает промис
  await flushPromises();
  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('Данные загружены');
});

Особенности использования

  • $nextTick следует вызывать после изменения реактивного состояния, а не до него.
  • Несколько последовательных изменений состояния можно объединять, вызвав $nextTick один раз после всех изменений, что ускоряет тесты и уменьшает количество промисов.
  • В компонентах с глубокими вложенными реактивными структурами $nextTick гарантирует обновление всех зависимых частей DOM.

Пример с несколькими изменениями:

test('несколько изменений данных', async () => {
  const wrapper = mount(Counter);

  wrapper.vm.count++;
  wrapper.vm.message = 'Привет';
  await wrapper.vm.$nextTick();

  expect(wrapper.text()).toContain('1');
  expect(wrapper.text()).toContain('Привет');
});

Альтернатива await wrapper.vm.$nextTick()

В некоторых случаях можно использовать метод wrapper.setData() или wrapper.setProps(), который автоматически ждёт обновления DOM и возвращает промис:

await wrapper.setData({ count: 1 });
expect(wrapper.text()).toContain('1');

Однако $nextTick остаётся универсальным инструментом, особенно когда изменения выполняются напрямую через vm или в пользовательских методах компонента.

Рекомендации

  • Использовать $nextTick для контроля точного момента обновления DOM после любых асинхронных или синхронных изменений.
  • Не полагаться на синхронные проверки состояния компонента после изменения реактивных данных.
  • Для сложных асинхронных сценариев комбинировать $nextTick с утилитами ожидания промисов.

await wrapper.vm.$nextTick() является ключевым инструментом для создания надёжных и стабильных тестов, которые проверяют реальные изменения DOM после всех обновлений Vue.