コンポーネントテスト
20.2.1. mount vs shallowMount
| 項目 | mount | shallowMount |
|---|---|---|
| 子コンポーネント | 実際にレンダリング | スタブ処理 |
| レンダリング範囲 | コンポーネントツリー全体 | 現在のコンポーネントのみ |
| 実行速度 | 比較的遅い | 高速 |
| 使用基準 | 統合テスト、子コンポーネントとの相互作用検証 | 単体テスト、現在のコンポーネントロジック検証 |
- 基本的に
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コマンドで更新します。 - 動的データ(日付、ランダム値など)はモック処理してスナップショットに含めません。
- コアロジックはスナップショットではなく明示的なアサーションで検証します。