Vue Test Utils (VTU) предоставляет инструменты для тестирования компонентов Vue, включая работу с реактивным состоянием, которое часто хранится в composables. Понимание того, как тестировать состояние и реактивность, является критически важным для надёжного покрытия тестами логики приложения.
Composables — это функции, возвращающие реактивные объекты, вычисляемые свойства и методы, которые инкапсулируют логику состояния. Основные инструменты Vue для работы с реактивностью:
ref() — создаёт реактивное примитивное значение.reactive() — создаёт реактивный объект или массив.computed() — создаёт вычисляемое свойство на основе
реактивных данных.watch() и watchEffect() — позволяют
отслеживать изменения реактивных данных.Пример простого composable для счётчика:
import { ref, computed } from 'vue';
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const double = computed(() => count.value * 2);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return { count, double, increment, decrement };
}
VTU не предоставляет прямого способа протестировать composable изолированно, поэтому часто composable оборачивают в тестовый компонент. Пример:
import { mount } from '@vue/test-utils';
import { defineComponent } from 'vue';
import { useCounter } from '@/composables/useCounter';
const TestComponent = defineComponent({
template: '<div></div>',
setup() {
return useCounter();
}
});
const wrapper = mount(TestComponent);
const { count, double, increment } = wrapper.vm;
Такой подход позволяет проверять начальное состояние, методы и вычисляемые свойства.
При тестировании важно учитывать реактивность Vue. Любое изменение реактивного значения должно проверяться после его обновления:
import { nextTick } from 'vue';
increment();
await nextTick();
expect(count.value).toBe(1);
expect(double.value).toBe(2);
nextTick() гарантирует, что все реактивные обновления
завершены перед проверкой состояния.reactive,
изменения также требуют nextTick() для корректного
отражения в тестах.Computed свойства вычисляются автоматически при изменении зависимостей. Проверка выглядит так:
count.value = 5;
await nextTick();
expect(double.value).toBe(10);
Watchers можно протестировать с помощью имитации изменения зависимых значений и проверки побочных эффектов:
import { watch } from 'vue';
let log = '';
watch(count, (newVal) => {
log = `count changed to ${newVal}`;
});
count.value = 3;
await nextTick();
expect(log).toBe('count changed to 3');
Для предотвращения влияния одного теста на другой, необходимо создавать новый экземпляр composable для каждого теста:
test('increment работает независимо', async () => {
const wrapper = mount(TestComponent);
const { count, increment } = wrapper.vm;
increment();
await nextTick();
expect(count.value).toBe(1);
});
Использование beforeEach позволяет гарантировать чистое
состояние:
let wrapper;
beforeEach(() => {
wrapper = mount(TestComponent);
});
Если composable содержит асинхронную логику, например
fetch или таймеры, тесты должны учитывать промисы и
ожидание реактивных обновлений:
async function useAsyncCounter() {
const count = ref(0);
async function incrementAsync() {
await new Promise(resolve => setTimeout(resolve, 100));
count.value++;
}
return { count, incrementAsync };
}
test('incrementAsync увеличивает count', async () => {
const wrapper = mount(defineComponent({
setup: useAsyncCounter,
template: '<div></div>'
}));
await wrapper.vm.incrementAsync();
await nextTick();
expect(wrapper.vm.count.value).toBe(1);
});
await nextTick() обеспечивает корректное обновление
реактивного состояния после асинхронных операций.nextTick после
изменений — особенно для объектов и массивов, созданных через
reactive.Эти методы позволяют глубоко проверять реактивное состояние в composables, обеспечивая высокое качество тестов и предсказуемость поведения компонентов Vue.