컴포넌트 테스트
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명령으로 업데이트합니다. - 동적 데이터(날짜, 랜덤 값 등)는 목 처리하여 스냅샷에 포함하지 않습니다.
- 핵심 로직은 스냅샷이 아닌 명시적 단언(assertion)으로 검증합니다.