Skip to content

Component Testing

20.2.1. mount vs shallowMount

ItemmountshallowMount
Child ComponentsActually renderedStubbed
Rendering ScopeEntire component treeCurrent component only
Execution SpeedRelatively slowFast
Usage CriteriaIntegration tests, verifying child interactionsUnit tests, verifying current component logic
  • Use mount by default.
  • Use shallowMount only 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 withDefaults are 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() and setValue() are asynchronous functions and must always be used with await.

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 -u command.
  • Dynamic data (dates, random values, etc.) must be mocked to exclude them from snapshots.
  • Core logic must be verified through explicit assertions, not snapshots.

TIENIPIA QUALIFIED STANDARD