Skip to content

コンポーネントテスト

20.2.1. mount vs shallowMount

項目mountshallowMount
子コンポーネント実際にレンダリングスタブ処理
レンダリング範囲コンポーネントツリー全体現在のコンポーネントのみ
実行速度比較的遅い高速
使用基準統合テスト、子コンポーネントとの相互作用検証単体テスト、現在のコンポーネントロジック検証
  • 基本的に mount を使用します。
  • 子コンポーネントの依存関係が複雑な場合にのみ shallowMount を使用します。

20.2.2. Props テスト

typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import AlertMessage from '../AlertMessage.vue'

describe('AlertMessage', () => {
  it('渡されたメッセージをレンダリングしなければなりません', () => {
    const wrapper = mount(AlertMessage, {
      props: { message: '保存が完了しました', type: 'success' },
    })
    expect(wrapper.text()).toContain('保存が完了しました')
    expect(wrapper.classes()).toContain('alert-success')
  })

  it('withDefaults のデフォルト値が正しく適用されなければなりません', () => {
    const wrapper = mount(AlertMessage, { props: { message: '通知' } })
    expect(wrapper.classes()).toContain('alert-info')
  })
})
  • withDefaults で設定したデフォルト値は、該当 prop を省略した状態で検証します。

20.2.3. Emits テスト

typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import ConfirmDialog from '../ConfirmDialog.vue'

describe('ConfirmDialog', () => {
  it('確認ボタンクリック時に confirm イベントを発生させなければなりません', async () => {
    const wrapper = mount(ConfirmDialog)
    await wrapper.find('[data-testid="btn-confirm"]').trigger('click')
    expect(wrapper.emitted('confirm')).toHaveLength(1)
  })

  it('イベントペイロードを正しく渡さなければなりません', async () => {
    const wrapper = mount(ConfirmDialog, { props: { itemId: 42 } })
    await wrapper.find('[data-testid="btn-confirm"]').trigger('click')
    expect(wrapper.emitted('confirm')![0]).toEqual([42])
  })
})
  • emitted() の戻り値はイベント名をキーとする配列であり、各要素はペイロード配列です。

20.2.4. イベントシミュレーション

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('入力値変更時に update:modelValue を発生させなければなりません', async () => {
    const wrapper = mount(SearchInput)
    await wrapper.find('input').setValue('検索語')
    expect(wrapper.emitted('update:modelValue')![0]).toEqual(['検索語'])
  })

  it('非同期状態変更後に DOM が更新されなければなりません', async () => {
    const wrapper = mount(SearchInput)
    await wrapper.find('[data-testid="btn-search"]').trigger('click')
    await nextTick()
    expect(wrapper.find('.results').exists()).toBe(true)
  })
})
  • trigger()setValue() は非同期関数であるため、必ず await を使用します。

20.2.5. スナップショットテスト

typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import UserCard from '../UserCard.vue'

describe('UserCard', () => {
  it('レンダリング結果がスナップショットと一致しなければなりません', () => {
    const wrapper = mount(UserCard, {
      props: { name: '홍길동', role: '開発者' },
    })
    expect(wrapper.html()).toMatchSnapshot()
  })
})
  • スナップショットは UI 構造の意図しない変更を検知する用途で使用します。
  • スナップショットファイルが変更された場合、vitest -u コマンドで更新します。
  • 動的データ(日付、ランダム値など)はモック処理してスナップショットに含めません。
  • コアロジックはスナップショットではなく明示的なアサーションで検証します。

TIENIPIA QUALIFIED STANDARD