В данном челендже, дается задача, дается минималистичная подсказка, и решение. В данном челендже я постараюсь пройти наиболее популярные задача Vue-test-utils и vitest.
Рекомендуется для усвоения материала, запустить vue приложение, и копируя компоненты, писать для них тесты. указанные в заданиях. Попутно читая в документации о методах и ранее не известных функциях.
1: Монтирование компонента и доступ к DOM
Компонент:
<template>
<div>{{ message }}</div>
</template>
<script setup>
defineProps({
message: String
});
</script>
Задание:
- Протестируй, что компонент отображает переданное значение
message. - Проверь, что если
messageне передано, то компонент будет работать.
Подсказка:
При проверке теста можно использовать: .toBe() .toEqual() .toContain()
- Используйте
.toBe()для примитивов (строки, числа, булевы) и ссылочного равенства - Используйте
.toEqual()для объектов, массивов и глубокого сравнения - Используйте
.toContain()для проверки наличия элемента в массиве или подстроки в строке
Используй mount, .text(), .exists().
text()- возвращает текстовое содержимое компонентаexists()- проверяет существует ли элементmount()mount()- монтирует Vue компонент в изолированное DOM-дерево
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import mountComponent from '@/components/mount.vue';
describe('mount.vue', () => {
it('Протестируй, что компонент отображает переданное значение message.', () => {
const wrapper = mount(mountComponent, {
props: {
message: 'message'
}
});
expect(wrapper.find('[data-test-message]').text()).toBe('message')
})
it('Проверь, что если message не передано, то ничего не падает', () => {
const wrapper = mount(mountComponent);
expect(wrapper.exists()).toBe(true)
})
})
2: Тестирование реактивного состояния
Компонент:
<template>
<button @click="count++">{{ count }}</button>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
Задание:
- Проверь, что начальное значение
countравно 0. - Проверь, что при клике значение увеличивается.
Подсказка:
Используй wrapper.get(), .trigger(), .text().
wrapper.get()- получает элемент с проверкой существования, аналогfind(), но выбрасывает ошибку, если элемент не найден, используется когда элемент должен существовать.wrapper.trigger()- симулирует DOM события, имитирует события на элементе (click, input, submit и т.д.)
Решение:
import { mount, trigger } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Counter from '@/components/Counter.vue';
describe('Counter',()=>{
it('Проверь, что начальное значение count равно 0.',()=>{
const wrapper = mount(Counter);
expect(wrapper.find('button').text()).toBe('0');
});
it('Проверь, что при клике 1 раз значение увеличивается.', async() => {
const wrapper = mount(Counter);
const button = wrapper.find('button');
await button.trigger('click');
expect(wrapper.find('button').text()).toBe('1');
});
it('Проверь, что при клике 3 раза значение увеличивается.', async () => {
const wrapper = mount(Counter);
const button = wrapper.find('button');
await button.trigger('click');
await button.trigger('click');
await button.trigger('click');
expect(wrapper.find('button').text()).toBe('3');
});
});
3: Тестирование emit’ов
Компонент:
<template>
<button @click="$emit('custom-event')">Click me</button>
</template>
<script setup>
defineEmits(['custom-event']);
</script>
Задание:
- Проверь, что при клике "эмитится" событие
custom-event.
Подсказка:
Используй wrapper.emitted().
wrapper.emitted() - отслеживает кастомные события, которые эмиттил компонент, возвращает объект со всеми событиями, которые были вызваны с помощью $emit() внутри компонента.
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Emits from '@/components/Emits.vue';
describe('Emits.vue',()=>{
it('Проверь, что при клике эмитится событие custom-event.', async ()=>{
const wrapper = mount(Emits);
const button = wrapper.find('button');
await button.trigger('click');
expect(wrapper.emitted('custom-event')).toBeTruthy();
expect(wrapper.emitted('custom-event')).toHaveLength(1) // доп проверка на колличество эмитов
});
})
4: Тестирование v-model
Компонент:
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)">
</template>
<script setup>
defineProps(['modelValue']);
defineEmits(['update:modelValue']);
</script>
Задание:
- Проверь, что инпут отображает переданное значение.
- Проверь, что при вводе эмитится обновление
modelValue.
Подсказка:
Используй wrapper.find('selector').setValue('value').
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Vmodel from '@/components/Vmodel.vue';
describe('Vmodel', () => {
it('Проверь, что инпут отображает переданное значение', async () => {
const wrapper = mount(Vmodel);
const input = wrapper.find('input');
await input.setValue('test');
expect(input.element.value).toBe('test');
});
it('Проверь, что при вводе эмитится обновление modelValue', async () => {
const wrapper = mount(Vmodel);
const input = wrapper.find('input');
await input.setValue('test');
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
expect(wrapper.emitted('update:modelValue')).toHaveLength(1);
expect(wrapper.emitted('update:modelValue')[0]).toBe(['test']);
})
})
5: Тестирование computed свойства
Компонент:
<template>
<div>{{ fullName }}</div>
</template>
<script setup>
import { computed } from 'vue';
const firstName = defineModel('firstName');
const lastName = defineModel('lastName');
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
</script>
Вызвать компонент так:
<script setup lang="ts">
import {ref} from 'vue';
import Computed from './components/Computed.vue';
const first = ref('Nick');
const last = ref('Mart');
</script>
<template>
<div>
<Computed v-model:firstName="first" v-model:lastName="last" />
</div>
</template>
Задание:
- Проверь, что
fullNameкорректно формируется. - Измените
firstNameи проверьте, чтоfullNameобновился.
Подсказка:
Используйте wrapper.setProps() и nextTick().
Имитирует изменение props, переданных от родительского компонента.
wrapper.setProps() - обновляет props компонента, имитирует изменение props, переданных от родительского компонента.
Пример использования: await wrapper.setProps({ propName: newValue })
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Computed from '@/components/Computed.vue';
describe('Computed.vue', () => {
it('Проверь, что fullName корректно формируется.', () => {
const wrapper = mount(Computed, {
props: {
firstName: 'Nick',
lastName: 'Tester'
}
});
expect(wrapper.find('[data-test="title"]').text()).toBe('Nick Tester')
});
it('Измените firstName и проверьте, что fullName обновился', async () => {
const wrapper = mount(Computed, {
props: {
firstName: 'Nick',
lastName: 'Tester'
}
});
await wrapper.setProps({
firstName: 'Vladimir',
lastName: 'Programmer'
});
expect(wrapper.find('[data-test="title"]').text()).toBe('Vladimir Programmer')
});
});
6: Тестирование watch и watchEffect
Компонент:
<template>
<div>{{ log }}</div>
</template>
<script setup>
import { ref, watch } from 'vue';
const count = ref(0);
const log = ref('');
watch(count, (val) => {
log.value = `Count changed to ${val}`;
});
</script>
Задание:
- Проверь, что при изменении
countобновляетсяlog.
Подсказка:
Используй прямой доступ к vm. Через vm изменить данные можно так: wrapper.vm.count = 10, и после этого нужен await wrapper.vm.$nextTick()
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Watch from '@/components/Watch.vue';
describe('Watch.vue', () => {
it('Проверь, что при изменении count обновляется log.',async()=>{
const wrapper = mount(Watch);
wrapper.vm.count = 1;
await wrapper.vm.$nextTick()
expect(wrapper.text()).toBe('Count changed to 1')
})
})
7: Использование stubs и mocks
Компонент:
<template>
<ChildComponent :data="someData" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const someData = 'hello';
</script>
Задание:
- "Застабь"
ChildComponentи проверь, что он получилdata.
stubs - заглушки для дочерних компонентов. stubs заменяют дочерние компоненты простыми заглушками во время тестирования. Это нужно для изоляции тестируемого компонента.
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Stub from '@/components/Stub.vue';
describe('Stub.vue', ()=>{
it('Делаю заглушку child и передаю внутрь props', async() =>{
const wrapper = mount(Stub,{
global:{
stubs: ['ChildComponent'],
}
});
const child = wrapper.find('child-component-stub');
expect(child.attributes('data')).toBe('hello');
})
});
Также можно вообще все дочерние компоненты заглушить через :
const wrapper = mount(TestComponent, {
shallow: true // Автоматически заглушает все дочерние компоненты
})
Или более новый и читабельный вариант shallowMount:
import { shallowMount } from '@vue/test-utils'
const wrapper = shallowMount(TestComponent, {
// опции конфигурации
})
8: Тестирование асинхронных данных
Компонент:
<template>
<div v-if="loading">Loading...</div>
<div v-else>{{ data }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const loading = ref(true);
const data = ref(null);
onMounted(async () => {
// simulate fetch
await new Promise(resolve => setTimeout(resolve, 100));
data.value = 'Loaded!';
loading.value = false;
});
</script>
Задание:
- Проверь, что сначала отображается "Loading...".
- После загрузки отображается "Loaded!".
Подсказка:
Используй flushPromises() и vi.advanceTimersByTime().
flushPromises - чтобы дождаться промиса, а vi.advanceTimersByTime чтобы перемотать setTimeout.
flushPromises() - "Прогоняет" все ожидающие Promise до их завершения. Заставляет JavaScript обработать все микрозадачи (microtasks) в очереди. По сути их закрывает.
Что НЕ делает flushPromises():
- Не ждет setTimeout/setInterval
- Не ждет requestAnimationFrame
- Не ждет HTTP-запросы (только Promise, которые уже разрешаются)
- Перемещает "виртуальное время" вперед на указанное количество миллисекунд, срабатывая для всех таймеров (setTimeout, setInterval). Пример:vi.advanceTimersByTime()
import { vi } from 'vitest'
// Включаем фейковые таймеры
vi.useFakeTimers();
setTimeout(() => console.log('Done!'), 1000);
vi.advanceTimersByTime(1000); // Прошло 1 секунда "виртуального" времени
// Выведет: "Done!"
flushPromises() - для .then(), async/await
vi.advanceTimersByTime() - для setTimeout, setInterval
Решение:
import { mount, flushPromises } from '@vue/test-utils';
import { describe, it, expect, vi } from 'vitest';
import Async from '@/components/Async.vue';
describe('Async.vue', () => {
it('Проверь, что сначала отображается "Loading..."', () => {
const wrapper = mount(Async);
expect(wrapper.text()).toBe('Loading...')
});
it('После загрузки отображается "Loaded!"', async () => {
vi.useFakeTimers();
const wrapper = mount(Async);
vi.advanceTimersByTime(1000);
await flushPromises();
expect(wrapper.text()).toBe('Loaded!')
});
})
9: Тестирование HTTP-запросов (моки)
Компонент:
<template>
<div>{{ user.name || 'No user' }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import axios from 'axios';
const user = ref({});
onMounted(async () => {
const response = await axios.get('https://jsonplaceholder.typicode.com/users/1');
user.value = response.data;
});
</script>
Задание:
- Замокай
axiosи верни фейкового юзера. - Проверь, что имя отображается.
Подсказка:
Используй vi.mock('axios') и axios.get.mockResolvedValue
import { mount, flushPromises } from '@vue/test-utils';
import { vi, expect, describe, it } from 'vitest';
import HTTP from '@/components/HTTP.vue';
import axios from 'axios';
vi.mock('axios');
describe('HTTP', () => {
it('Замокай axios и верни фейкового юзера.', async () => {
const user = { name: "Nick Molodets" };
axios.get.mockResolvedValue({
data: user,
status: 200
});
const wrapper = mount(HTTP);
await flushPromises();
await wrapper.vm.$nextTick();
expect(wrapper.text()).toBe('Nick Molodets');
});
});
10: Тестирование форм и валидации
Компонент:
<template>
<form @submit.prevent="submit">
<input v-model="email" type="email" required />
<span v-if="error">{{ error }}</span>
<button type="submit">Submit</button>
</form>
</template>
<script setup>
import { ref } from 'vue';
const email = ref('');
const error = ref('');
const submit = () => {
if (!email.value.includes('@')) {
error.value = 'Invalid email';
}
};
</script>
Задание:
- Проверь, что при неверном email появляется ошибка.
- Проверь, что при правильном email ошибка исчезает.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import Form from '@/components/Form.vue';
describe('Form.vue', ()=>{
it('Проверь, что при неверном email появляется ошибка', async()=>{
const wrapper = mount(Form);
const input = wrapper.find('input');
const form = wrapper.find('form');
await input.setValue('test');
await form.trigger('submit');
expect(wrapper.find('span').text()).toBe('Invalid email')
});
it('Проверь, что при правильном email ошибка исчезает.', async () => {
const wrapper = mount(Form);
const input = wrapper.find('input');
const form = wrapper.find('form');
await input.setValue('test@mail.ru');
await form.trigger('submit');
expect(wrapper.find('span').exists()).toBe(false);
})
})
11: Тестирование глобальных свойств (например, $t)
Создайте файл /src/plugins/i18n.js
import { createI18n } from 'vue-i18n'
const messages = {
en: {
welcome: 'Welcome!',
greeting: 'Hello, {name}!'
},
ru: {
welcome: 'Добро пожаловать!',
greeting: 'Привет, {name}!'
}
}
const i18n = createI18n({
legacy: false,
locale: 'ru',
fallbackLocale: 'en',
messages
})
export default i18n
Подключите в main.js/main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import i18n from './plugins/i18n'
createApp(App).use(i18n).mount('#app')
Компонент:
<template>
<h1>{{ $t('welcome') }}</h1>
</template>
Задание:
- Замокай
$tи проверь, что отображается правильный текст.
Подсказка: Используй global.mocks.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import MockT from '@/components/MockT.vue';
describe('MockT', () => {
it('Замокай $t и проверь, что отображается правильный текст.', () => {
const wrapper = mount(MockT, {
global:{
mocks:{
$t:()=> 'Hello Nick'
}
}
});
expect(wrapper.text()).toBe('Hello Nick');
})
})
12: Тестирование provide / inject
Компонент:
<template>
<div>{{ injectedValue }}</div>
</template>
<script setup>
import { inject } from 'vue';
const injectedValue = inject('myKey');
</script>
Задание:
- Проверь, что компонент получает значение через
provide.
Подсказка:
Используй global.provide.
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest'
import Provide from '@/components/Provide.vue';
describe('Provide',()=>{
it('Проверь, что компонент получает значение через provide.',()=>{
const wrapper = mount(Provide,{
global:{
provide:{
myKey:'test'
}
}
});
expect(wrapper.text()).toBe('test')
})
})
13: Тестирование Pinia store
Компонент:
<template>
<div>{{ counterStore.count }}</div>
</template>
<script setup>
import { useCounterStore } from '@/stores/counter';
const counterStore = useCounterStore();
</script>
Задание:
- Замокай store и проверь, что отображается нужное значение.
Подсказка:
Используй vi.mock('@/stores/counter').
vi.fn() создает "заглушку" функции, которая:
- Заменяет реальную функцию
- Позволяет отслеживать её вызовы
- Может возвращать заданные значения
Решение:
import { mount } from '@vue/test-utils';
import { describe, expect, it, vi } from 'vitest'
import Pinia from '@/components/Pinia.vue';
import { useCounterStore } from '@/stores/counter'
describe('Pinia', () => {
it('Замокай store и проверь, что отображается нужное значение.', () => {
vi.mock('@/stores/counter', ()=>({
useCounterStore:vi.fn(()=>({
count:54
}))
}))
const wrapper = mount(Pinia);
expect(wrapper.text()).toBe('54')
})
})
14: Тестирование маршрутов (Router)
Компонент:
<template>
<div>{{ $route.params.id }}</div>
</template>
Задание:
- Замокай
$routeи проверь отображение параметра.
Подсказка:
Используй global.mocks.
Подготовка:
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Router from '@/components/Router.vue'; // твой компонент
const routes = [
{
path: '/test/:id', // :id — динамический параметр
name: 'ItemDetail',
component: Router,
},
];
export const router = createRouter({
history: createWebHistory(),
routes,
});
//main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { router } from './router';
createApp(App).use(router).mount('#app')
И теперь по урлу - /test/88 на странице будет текст -88
Решение:
import { mount } from '@vue/test-utils';
import { vi, it, describe, expect } from 'vitest';
import Router from '@/components/Router.vue';
describe('Router.vue', () => {
it('амокай $route и проверь отображение параметра.', () => {
const wrapper = mount(Router, {
global: {
mocks: {
$route: {
params: { id: '12345' }
}
}
}
});
expect(wrapper.text()).toBe('12345');
})
})
15: Тестирование условного рендеринга
Компонент:
<template>
<div v-if="show">Visible</div>
<div v-else>Hidden</div>
</template>
<script setup>
defineProps(['show']);
</script>
Задание:
- Проверь, что при
show=trueвидно "Visible". - При
show=false— "Hidden".
Подсказка:
Используй setProps.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import Vif from '@/components/V-if.vue';
describe('V-if.vue', () => {
it('Проверь, что при show=true видно "Visible"', () => {
const wrapper = mount(Vif, {
props: {
show: true
}
});
expect(wrapper.text()).toBe('Visible');
});
it('При show=false — "Hidden".', () => {
const wrapper = mount(Vif, {
props: {
show: false
}
});
expect(wrapper.text()).toBe('Hidden');
})
})
16: Тестирование списков (v-for)
Компонент:
<template>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script setup>
defineProps(['items']);
</script>
Задание:
- Проверь, что отображаются все элементы списка.
Подсказка:
Используй wrapper.findAll.
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import vfor from '@/components/v-for.vue';
describe('v-for.vue', () => {
const fakeItems = [{ id: 1, name: 'vfor1' }, { id: 2, name: 'vfor2' }];
it('Проверь, что отображаются все элементы списка.', () => {
const wrapper = mount(vfor, {
props: {
items: fakeItems
}
});
const items = wrapper.findAll('li');
expect(items).toHaveLength(2);
console.log(items);
expect(items[0].text()).toBe('vfor1');
})
})
17: Тестирование scoped slots
Компонент:
<template>
<slot name="header" :title="title"></slot>
</template>
<script setup>
const title = 'My Title';
</script>
Задание:
- Проверь, что слот получает
title.
Подсказка:
Используй slots.
test('passes title to slot', () => {
const wrapper = mount(MyComponent, {
slots: {
header: `<template #header="{ title }">{{ title }}</template>`
}
});
expect(wrapper.text()).toBe('My Title');
});
18: Тестирование lifecycle hooks
Компонент:
<template>
<div>{{ mounted }}</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const mounted = ref(false);
onMounted(() => {
mounted.value = true;
});
</script>
Задание:
- Проверь, что
mountedстановитсяtrueпосле монтирования.
Подсказка:
Это происходит автоматически при mount, а достучаться до переменной можно через wrapper.vm.mounted
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import Life from '@/components/Life.vue';
describe('Life.vue', () => {
it('mounted становится true после монтирования', () => {
const wrapper = mount(Life);
expect(wrapper.vm.mounted).toBe(true);
})
})
19: Тестирование асинхронных хуков
Компонент:
<template>
<div>{{ data }}</div>
</template>
<script setup>
import { ref } from 'vue';
const data = ref('initial');
// Async hook
setTimeout(() => {
data.value = 'async data';
}, 100);
</script>
Задание:
- Проверь, что
dataменяется послеsetTimeout.
Подсказка:
Используй vi.advanceTimersByTime. Этот метод будет запускать каждый инициированный таймер до тех пор, пока не истечет указанное количество миллисекунд или пока очередь не опустеет — в зависимости от того, что произойдет раньше.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it, vi } from 'vitest';
import AsyncHook from '@/components/AsyncHook.vue';
describe('AsyncHook.vue', () => {
vi.useFakeTimers();
it('data меняется после setTimeout.', async () => {
const wrapper = mount(AsyncHook);
await vi.advanceTimersByTime(100);
expect(wrapper.text()).toBe('async data');
});
})
20: Тестирование с использованием composables
Компонент:
<template>
<div>{{ useMyComposable().value }}</div>
</template>
<script setup>
import { useMyComposable } from '@/composables/myComposable';
</script>
// composables/myComposable.ts
import { ref } from 'vue'
export function useMyComposable() {
// Создаем реактивную переменную
const message = ref('Привет от композабла!')
// Возвращаем объект с данными
return message
}
Задание:
- Замокай composable и проверь его возвращаемое значение.
Подсказка:
Используй vi.mock.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import Composable from '@/components/Composable.vue';
describe('Composable.vue', () => {
it('Замокай composable и проверь его возвращаемое значение', () => {
vi.mock('@/composables/myComposable', () => ({
useMyComposable: () => ({
value: 'Привет от замоканного компосаблa'
});
}));
const wrapper = mount(Composable);
expect(wrapper.text()).toBe('Привет от замоканного компосаблa');
})
})
21: Тестирование событий мыши/клавиатуры
Компонент:
<template>
<div @keydown="handleKeyDown" tabindex="0">-->{{ key }}</div>
</template>
<script setup>
import { ref } from 'vue';
const key = ref('');
const handleKeyDown = (e) => {
key.value = e.key;
};
</script>
Задание:
- Симулируй нажатие клавиши и проверь, что она отображается.
Подсказка:
Используй trigger('keydown', { key: 'Enter' }).
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import KeyDown from '@/components/KeyDown.vue';
describe('KeyDown.vue', () => {
it('Симулируй нажатие клавиши и проверь, что она отображается.', async () => {
const wrapper = mount(KeyDown);
await wrapper.trigger('keydown', { key: 'Enter' });
expect(wrapper.text()).toBe('-->Enter');
});
})
22: Тестирование Drag & Drop
Компонент:
<template>
<div @dragstart="dragStart">Drag me</div>
</template>
<script setup>
const dragStart = (e) => {
e.dataTransfer.setData('text/plain', 'dragged data');
};
</script>
Задание:
- Проверь, что при драге устанавливается правильный тип и данные.
Подсказка:
Нужно замокать dataTransfer.
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import drag from '@/components/drag.vue';
describe('drag.vue', () => {
it('при драге устанавливается правильный тип и данные.', async () => {
const dataTransfer = {
setData: vi.fn()
};
const wrapper = mount(drag);
const div = wrapper.find('div');
div.trigger('dragstart', {
dataTransfer
});
expect(dataTransfer.setData).toHaveBeenCalledTimes(1)
expect(dataTransfer.setData).toHaveBeenCalledWith('text/plain', 'dragged data')
});
})
23: Тестирование ошибок (try/catch)
Компонент:
<template>
<div v-if="error">Error: {{ error.message }}</div>
<div v-else>Success</div>
</template>
<script setup>
import { ref } from 'vue';
const error = ref(null);
try {
throw new Error('Test error');
} catch (e) {
error.value = e;
}
</script>
Задание:
- Проверь, что отображается сообщение об ошибке.
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import tryComp from '@/components/try.vue';
describe('tryComp.vue', () => {
it('Проверь, что отображается сообщение об ошибке.', async () => {
const wrapper = mount(tryComp);
expect(wrapper.text()).toContain('Error: Test error');
});
})
24: Тестирование Watch с deep и immediate
Компонент:
<template>
<div>{{ log }}</div>
</template>
<script setup>
import { ref, reactive, watch } from 'vue';
const obj = reactive({ nested: { value: 1 } });
const log = ref('');
watch(
() => obj.nested,
(val) => {
log.value = `Changed to ${val.value}`;
},
{ deep: true, immediate: true }
);
</script>
Задание:
- Проверь, что срабатывает сразу и при изменении
obj.nested.value.
Подсказка:
Используй nextTick
Решение:
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import watch from '@/components/watch.vue';
describe('watch.vue', () => {
it('Проверь, что отображается сообщение об ошибке.', async () => {
const wrapper = mount(drag);
wrapper.vm.obj.nested.value = '3';
await wrapper.vm.$nextTick();
expect(wrapper.text()).toBe('Changed to 3');
});
})
25: Тестирование компонента с Teleport
Компонент:
<template>
<Teleport to="#modal">
<div>Modal content</div>
</Teleport>
</template>
//App.vue
<script setup lang="ts">
import {ref} from 'vue';
import Tele from '@/components/Teleport.vue';
</script>
<template>
<div id="modal"></div>
<div>
<Tele />
</div>
</template>
Задание:
- Проверь, что содержимое попало в нужный контейнер.
Подсказка:
Создай div с id="modal", передай в attachTo.
import { mount } from '@vue/test-utils';
import { expect, describe, it } from 'vitest';
import Tele from '@/components/Teleport.vue';
describe('Teleport.vue', () => {
it('Проверь, что содержимое попало в нужный контейнер.', async () => {
document.body.innerHTML = '<div id="modal"></div>';
const wrapper = mount(Tele, { attachTo: document.body });
console.log(document.body.innerHTML); //<div id="modal"><div>Modal content</div></div><div data-v-app=""><!--teleport start--><!--teleport end--></div>
expect(document.querySelector('#modal div').textContent).toBe('Modal content');
});
});
26: Тестирование с асинхронным компонентом
Компонент:
<template>
<Suspense>
<AsyncComponent />
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
</script>
//AsyncComponent.vue
<script setup>
import {ref} from 'vue';
const text = ref('hello')
</script>
<template>
{{ text }}
</template>
Задание:
- Проверь, что компонент загружается.
Подсказка:
Мокай импорт через Stub и дождись всех промисов через flushPromises.
import { mount, flushPromises } from '@vue/test-utils';
import { describe, it, expect, vi } from 'vitest';
import Acomponent from '@/components/Acomponent.vue';
describe('Acomponent.vue', () => {
it('Проверь, что компонент загружается', async () => {
const wrapper = mount(Acomponent, {
global: {
stubs: {
// Автоматически создаст стаб для компонента
AsyncComponent: {
template: '<div>Stubbed async component</div>'
}
}
}
});
await flushPromises();
expect(wrapper.text()).toBe('Stubbed async component');
});
});
27: Тестирование с использованием describe и beforeEach
Компонент:
<template>
<div>{{ value }}</div>
</template>
<script setup>
defineProps(['value']);
</script>
Задание:
- Напиши несколько тестов с общими настройками.
Подсказка:
Используй beforeEach для подготовки wrapper.
import { mount, flushPromises } from '@vue/test-utils';
import { describe, it, expect, vi } from 'vitest';
import Desc from '@/components/describe.vue';
describe('describe.vue', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Desc, { props: { value: 'default' } });
});
it('значение по умолчанию', () => {
expect(wrapper.text()).toBe('default')
});
it('меняем значение по умолчанию', async () => {
await wrapper.setProps({ value: 'ttest2' });
expect(wrapper.text()).toBe('ttest2');
})
})