watch и watchEffectВ Vue 3 watch и watchEffect являются
основными инструментами реактивного наблюдения за состоянием.
watch отслеживает конкретные реактивные источники, тогда
как watchEffect автоматически отслеживает все реактивные
зависимости, использованные внутри колбэка. При тестировании composables
важно учитывать:
nextTick
или асинхронных ожиданий (await) в тестах.mount или
shallowMount для обертки небольшого хука внутри тестового
компонента.Для тестирования composables с watch и
watchEffect часто создается минимальный тестовый
компонент:
import { defineComponent } from 'vue';
import { mount } from '@vue/test-utils';
import useExample from '@/composables/useExample';
const TestComponent = defineComponent({
template: `<div></div>`,
setup() {
return useExample();
}
});
Использование mount позволяет watchEffect
корректно срабатывать, так как Vue требует реактивного контекста
компонента для запуска эффектов.
watchwatch реагирует на изменения конкретных реактивных
источников. Основные шаги:
ref или
reactive).watch, который выполняет
необходимые действия при изменении источника.Пример:
import { ref, watch } from 'vue';
export default function useCounter() {
const count = ref(0);
const doubled = ref(0);
watch(count, (newValue) => {
doubled.value = newValue * 2;
});
return { count, doubled };
}
Тест:
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import useCounter from '@/composables/useCounter';
import { defineComponent } from 'vue';
const TestComponent = defineComponent({
template: '<div></div>',
setup() {
return useCounter();
}
});
test('watch correctly doubles count', async () => {
const wrapper = mount(TestComponent);
const { count, doubled } = wrapper.vm;
count.value = 5;
await nextTick();
expect(doubled.value).toBe(10);
});
Ключевой момент: nextTick необходим для
того, чтобы обновления реактивных данных, отслеживаемых
watch, успели сработать до проверки значений.
watchEffectwatchEffect запускается сразу при создании и реагирует
на все используемые реактивные зависимости. В тестах важно:
Пример:
import { ref, watchEffect } from 'vue';
export default function useLogger() {
const message = ref('');
const log = ref([]);
watchEffect(() => {
if (message.value) {
log.value.push(message.value);
}
});
return { message, log };
}
Тест:
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import useLogger from '@/composables/useLogger';
import { defineComponent } from 'vue';
const TestComponent = defineComponent({
template: '<div></div>',
setup() {
return useLogger();
}
});
test('watchEffect logs message updates', async () => {
const wrapper = mount(TestComponent);
const { message, log } = wrapper.vm;
expect(log.value).toEqual([]);
message.value = 'Hello';
await nextTick();
expect(log.value).toEqual(['Hello']);
message.value = 'World';
await nextTick();
expect(log.value).toEqual(['Hello', 'World']);
});
Особенность watchEffect: эффект
запускается сразу при монтировании, поэтому начальные проверки должны
учитывать это поведение.
watch с опцией { immediate: true }
выполняет колбэк при инициализации, что нужно учитывать при тестировании
начального состояния.watch работает
только на изменения самих ссылок, а не глубоких свойств, если не указан
параметр { deep: true }.watchEffect автоматически отслеживает все реактивные
свойства, используемые внутри, включая вложенные объекты, что упрощает
тестирование сложных composables.watch или
watchEffect требуют использования
flushPromises или await nextTick(), чтобы
дождаться завершения эффекта перед проверкой результата.mount или shallowMount
позволяет полностью протестировать эффекты composables в реактивном
контексте.jest.useFakeTimers() и
jest.advanceTimersByTime(), если watch или
watchEffect содержит setTimeout или подобные
задержки.console.log, API вызовы,
изменение других реактивных переменных) выполняется через spy-функции
(jest.fn()).await nextTick() после изменения
реактивного источника перед проверкой состояния.{ deep: true } в
watch, чтобы корректно отлавливать изменения вложенных
свойств.Тщательное тестирование watch и watchEffect
позволяет обнаружить ошибки реактивности на ранней стадии, обеспечивает
предсказуемость поведения composables и предотвращает неожиданные
побочные эффекты при масштабировании приложений.