В Vue Test Utils тестирование компонентов строится вокруг рендеринга компонентов в изолированной среде и проверки их поведения. Ключевой принцип — один assert на тест или аккуратная группировка проверок, что обеспечивает ясность и поддержку тестов в долгосрочной перспективе.
Практика написания одного утверждения (assert) на тест помогает:
Пример компонента Counter.vue:
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
}
}
</script>
Тест с одним assert:
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'
test('счетчик увеличивается при клике', async () => {
const wrapper = mount(Counter)
const button = wrapper.find('button')
await button.trigger('click')
expect(wrapper.text()).toContain('1')
})
Здесь тест проверяет только одно поведение — увеличение счетчика. Если тест провалится, причина сразу ясна.
Когда проверок много, их объединяют в логические блоки с помощью
describe и нескольких it или
test. Это сохраняет принцип одного assert на
тест, но позволяет структурировать проверку более сложного
поведения компонента.
Пример с компонентом формы LoginForm.vue:
<template>
<form @submit.prevent="submit">
<input v-model="username" placeholder="Username" />
<input type="password" v-model="password" placeholder="Password" />
<button type="submit">Login</button>
<p v-if="error">{{ error }}</p>
</form>
</template>
<script>
export default {
data() {
return {
username: '',
password: '',
error: ''
}
},
methods: {
submit() {
if (!this.username || !this.password) {
this.error = 'Все поля обязательны'
} else {
this.error = ''
}
}
}
}
</script>
Группировка тестов:
import { mount } from '@vue/test-utils'
import LoginForm from './LoginForm.vue'
describe('LoginForm', () => {
let wrapper
beforeEach(() => {
wrapper = mount(LoginForm)
})
test('показывает ошибку, если username пуст', async () => {
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('Все поля обязательны')
})
test('показывает ошибку, если password пуст', async () => {
await wrapper.setData({ username: 'user' })
await wrapper.find('button').trigger('click')
expect(wrapper.text()).toContain('Все поля обязательны')
})
test('не показывает ошибку при заполнении всех полей', async () => {
await wrapper.setData({ username: 'user', password: '1234' })
await wrapper.find('button').trigger('click')
expect(wrapper.text()).not.toContain('Все поля обязательны')
})
})
Каждый test содержит один assert, а
describe объединяет их по смыслу. Такая структура
позволяет:
beforeEach для общих
настроек компонентов.describe по
функциональным блокам или сценариям.data-testid вместо общих тегов или классов для устойчивости
тестов.await с trigger, setData или
nextTick, чтобы тест проверял фактическое состояние
компонента.Vue Test Utils позволяет проверять события через
emitted(), а данные компонента — через vm или
text(). Принцип один assert сохраняется:
test('emit login событие с правильными данными', async () => {
const wrapper = mount(LoginForm)
await wrapper.setData({ username: 'user', password: '1234' })
await wrapper.find('button').trigger('click')
expect(wrapper.emitted().login[0]).toEqual([{ username: 'user', password: '1234' }])
})
Тест проверяет только факт генерации события с правильными аргументами.
describe позволяет
структурировать тесты крупных компонентов.test.Следование этим принципам делает тесты во Vue Test Utils надежными, поддерживаемыми и легко расширяемыми, что критично при работе с большими фронтенд-проектами.