Интеграция Vue Test Utils с TypeScript начинается с корректной конфигурации проекта. Ключевая цель — обеспечить строгую типизацию компонентов, тестовых утилит и вспомогательных функций без ухудшения DX и увеличения количества приведений типов.
Базовый набор зависимостей включает:
typescriptvue@vue/test-utilsvitest или jest@types/jest, если используется
Jest)Файл tsconfig.json должен учитывать специфику
тестов:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"types": ["vitest/globals", "node"],
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src", "tests"]
}
Важно, чтобы каталог с тестами входил в область компиляции. Это позволяет TypeScript анализировать типы компонентов, моков и хелперов.
Vue Test Utils предоставляет обобщённый тип
VueWrapper<T>, где T — тип экземпляра
компонента. При использовании mount или
shallowMount TypeScript способен вывести тип автоматически,
если компонент объявлен корректно.
import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
const wrapper = mount(MyComponent)
В этом случае wrapper.vm имеет тип
ComponentPublicInstance, но без уточнения пропсов и
методов. Для более строгой типизации используется явное указание
типа:
const wrapper = mount<typeof MyComponent>(MyComponent)
Теперь wrapper.vm содержит публичные свойства
компонента, включая props, computed,
methods, emits.
TypeScript особенно полезен при тестировании контрактов компонента.
Компонент с типизированными пропсами:
export default defineComponent({
props: {
count: {
type: Number,
required: true
}
},
emits: {
increment: (value: number) => value > 0
}
})
При монтировании:
mount(MyComponent, {
props: {
count: 5
}
})
Передача некорректного типа пропса приводит к ошибке компиляции. Аналогично, при проверке событий:
wrapper.vm.$emit('increment', 'wrong')
TypeScript сигнализирует о несоответствии сигнатуре
emits.
wrapper.vm и публичным APIПо умолчанию Vue Test Utils предоставляет доступ только к публичному API компонента. Это совпадает с философией Vue 3 и хорошо сочетается с TypeScript.
Для компонентов, использующих setup, важно возвращать
свойства, необходимые для тестов:
setup() {
const value = ref(0)
const inc = () => value.value++
return { value, inc }
}
В тестах:
wrapper.vm.inc()
expect(wrapper.vm.value).toBe(1)
TypeScript точно знает типы value и inc,
без any и без приведения типов.
find и
работы с DOMМетоды find и findAll возвращают обобщённые
типы:
DOMWrapper<Element>VueWrapper<ComponentPublicInstance>Пример:
const button = wrapper.find('button')
button.trigger('click')
При необходимости доступа к конкретному элементу:
const input = wrapper.find('input').element as HTMLInputElement
expect(input.value).toBe('text')
Приведение типов допустимо только на уровне DOM, а не компонентов. Это сохраняет строгую типизацию бизнес-логики.
Vue Test Utils поддерживает типизированные слоты через
slots:
mount(MyComponent, {
slots: {
default: '<span>Content</span>'
}
})
Для scoped slots:
slots: {
default: (props: { value: number }) => `Value: ${props.value}`
}
TypeScript проверяет сигнатуру слота, если компонент корректно типизирован.
При использовании global конфигурации важно сохранять
типовую согласованность:
mount(MyComponent, {
global: {
components: { BaseButton },
plugins: [router]
}
})
Для плагинов с расширением app.config.globalProperties
рекомендуется объявлять глобальные типы:
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$api: ApiClient
}
}
После этого $api доступен в wrapper.vm с
корректным типом.
Vue Test Utils позволяет использовать vi.fn() или
jest.fn() с полной типизацией:
const onCl ick = vi.fn<(value: number) => void>()
При передаче в props:
mount(MyComponent, {
props: {
onClick
}
})
TypeScript гарантирует совпадение сигнатуры функции с ожиданиями компонента.
Для стабов компонентов:
stubs: {
ChildComponent: {
template: '<div />'
}
}
Если требуется типизация интерфейса дочернего компонента, предпочтительнее использовать реальный компонент или функциональный стаб с явным типом пропсов.
nextTickАсинхронные обновления Vue корректно типизируются:
await wrapper.vm.$nextTick()
nextTick возвращает Promise<void>,
что упрощает цепочки ожиданий и предотвращает ошибки синхронизации.
Для повторного использования логики создаются типизированные фабрики:
function factory(props?: Partial<Props>) {
return mount(MyComponent, {
props: {
count: 1,
...props
}
})
}
Partial<Props> позволяет передавать только
необходимые параметры, сохраняя контроль типов.
Интеграция Vue Test Utils с TypeScript делает тесты частью контрактной системы приложения. Ошибки в пропсах, событиях и публичных методах выявляются на этапе компиляции. Это снижает количество неявных допущений, упрощает рефакторинг и делает тесты устойчивыми к изменению внутренней реализации компонентов.