Component Testing
20.2.1. mount vs shallowMount
| Item | mount | shallowMount |
|---|---|---|
| Child Components | Actually rendered | Stubbed |
| Rendering Scope | Entire component tree | Current component only |
| Execution Speed | Relatively slow | Fast |
| Usage Criteria | Integration tests, verifying child interactions | Unit tests, verifying current component logic |
- Use
mountby default. - Use
shallowMountonly when child component dependencies are complex.
20.2.2. Props Testing
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import AlertMessage from '../AlertMessage.vue'
describe('AlertMessage', () => {
it('should render the provided message', () => {
const wrapper = mount(AlertMessage, {
props: { message: 'Save completed successfully', type: 'success' },
})
expect(wrapper.text()).toContain('Save completed successfully')
expect(wrapper.classes()).toContain('alert-success')
})
it('should correctly apply withDefaults default values', () => {
const wrapper = mount(AlertMessage, { props: { message: 'Notice' } })
expect(wrapper.classes()).toContain('alert-info')
})
})- Default values set with
withDefaultsare verified by omitting the corresponding prop.
20.2.3. Emits Testing
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import ConfirmDialog from '../ConfirmDialog.vue'
describe('ConfirmDialog', () => {
it('should emit a confirm event when the confirm button is clicked', async () => {
const wrapper = mount(ConfirmDialog)
await wrapper.find('[data-testid="btn-confirm"]').trigger('click')
expect(wrapper.emitted('confirm')).toHaveLength(1)
})
it('should pass the event payload correctly', async () => {
const wrapper = mount(ConfirmDialog, { props: { itemId: 42 } })
await wrapper.find('[data-testid="btn-confirm"]').trigger('click')
expect(wrapper.emitted('confirm')![0]).toEqual([42])
})
})- The
emitted()return value is an array keyed by event name, where each element is a payload array.
20.2.4. Event Simulation
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import SearchInput from '../SearchInput.vue'
describe('SearchInput', () => {
it('should emit update:modelValue when input value changes', async () => {
const wrapper = mount(SearchInput)
await wrapper.find('input').setValue('search term')
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['search term'])
})
it('should update the DOM after asynchronous state changes', async () => {
const wrapper = mount(SearchInput)
await wrapper.find('[data-testid="btn-search"]').trigger('click')
await nextTick()
expect(wrapper.find('.results').exists()).toBe(true)
})
})trigger()andsetValue()are asynchronous functions and must always be used withawait.
20.2.5. Snapshot Testing
typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import UserCard from '../UserCard.vue'
describe('UserCard', () => {
it('should match the rendered result with the snapshot', () => {
const wrapper = mount(UserCard, {
props: { name: 'John Doe', role: 'Developer' },
})
expect(wrapper.html()).toMatchSnapshot()
})
})- Snapshots are used to detect unintended changes to UI structure.
- When a snapshot file changes, update it using the
vitest -ucommand. - Dynamic data (dates, random values, etc.) must be mocked to exclude them from snapshots.
- Core logic must be verified through explicit assertions, not snapshots.