Vitest와 Testing Library를 사용한 Svelte 및 Vue 단위 테스트
Wenhao Wang
Dev Intern · Leapcell

소개
빠르게 변화하는 프론트엔드 개발 세계에서 애플리케이션의 안정성과 유지보수성을 보장하는 것이 가장 중요합니다. Svelte와 Vue는 우아한 구문과 성능 이점으로 계속해서 인기를 얻고 있으며, 강력한 테스트 전략의 필요성이 더욱 중요해지고 있습니다. 특히 단위 테스트는 코드베이스의 작고 독립적인 부분을 분리하고 검증할 수 있게 해주는 첫 번째 방어선 역할을 합니다. 이 실천은 버그를 조기에 발견할 뿐만 아니라 살아있는 문서 역할도 하며 리펙토링에 대한 자신감을 심어줍니다. 이 글에서는 Vitest, 즉 매우 빠른 테스트 프레임워크와 사용자 중심 테스트 유틸리티인 Testing Library를 효과적으로 활용하여 Svelte 및 Vue 애플리케이션에 대한 의미 있는 단위 테스트를 작성하는 방법을 탐구합니다. 설정을 살펴보고, 핵심 원칙을 알아보고, 실용적인 예제를 통해 테스트 실력을 향상시킬 것입니다.
핵심 테스트 개념 및 도구
구현 세부 사항을 자세히 알아보기 전에 관련 핵심 기술에 대한 공통된 이해를 확립해 보겠습니다.
-
단위 테스트: 소프트웨어의 개별 단위 또는 구성 요소를 테스트하는 소프트웨어 테스트 방법입니다. 목적은 소프트웨어의 각 단위가 설계대로 작동하는지 확인하는 것입니다. Svelte 및 Vue와 같은 프론트엔드 프레임워크에서 "단위"는 종종 단일 구성 요소, 유틸리티 함수 또는 저장소 모듈을 참조합니다.
-
Vitest: Vite를 기반으로 구축된 차세대 테스트 프레임워크입니다. 테스트를 위한 즉각적인 핫 모듈 리로딩, 네이티브 ES 모듈 지원 및 놀랍도록 빠른 테스트 러너와 같은 기능을 자랑합니다. Vite 기반 Svelte 및 Vue 프로젝트와의 원활한 통합은 현대 프론트엔드 테스트에 이상적인 선택입니다.
-
Testing Library: 소프트웨어가 사용되는 방식과 유사하게 테스트를 작성할수록 더 큰 확신을 얻을 수 있다는 원칙을 바탕으로 UI 구성 요소를 사용자 중심 방식으로 테스트하는 데 도움이 되는 유틸리티 세트입니다. 구성 요소 내부(상태 또는 props와 같은)와 직접 상호 작용하는 대신, 사용자가 하는 것처럼 DOM을 쿼리하도록 권장하여 보다 탄력적이고 덜 깨지기 쉬운 테스트를 촉진합니다.
-
Svelte Testing Library / Vue Testing Library: 이들은 핵심 Testing Library를 감싸는 프레임워크별 래퍼로, 각각 Svelte 및 Vue 구성 요소에 맞춤화된 유틸리티 함수를 제공합니다. 이를 통해 구성 요소를 마운트하고 렌더링된 출물과 상호 작용하는 것이 간단해집니다.
테스트 환경 설정
Svelte 및 Vue 프로젝트 모두에 대한 초기 설정을 살펴보겠습니다.
Svelte 애플리케이션 설정
Vite로 초기화된 Svelte 프로젝트(예: npm create vite@latest my-svelte-app -- --template svelte-ts
)가 있다고 가정하고 필요한 테스트 종속성을 설치합니다.
npm install -D vitest @testing-library/svelte @testing-library/dom jsdom
vite.config.js
(또는 vite.config.ts
) 파일에서 vitest
를 구성합니다.
// vite.config.ts import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ plugins: [svelte()], test: { environment: 'jsdom', // 브라우저와 유사한 환경을 위해 JSDOM 사용 globals: true, // expect, describe, it와 같은 전역 API를 사용할 수 있도록 설정 setupFiles: './setupTests.ts', // 선택 사항: 전역 설정용, 예: expect 확장 }, })
setupTests.ts
(선택 사항이지만 좋은 방법)를 만듭니다.
// setupTests.ts import '@testing-library/jest-dom/vitest';
이제 테스트를 작성할 준비가 되었습니다.
Vue 애플리케이션 설정
마찬가지로 Vite로 초기화된 Vue 프로젝트(예: npm create vue@latest my-vue-app
)의 경우 종속성을 설치합니다.
npm install -D vitest @vue/test-utils @testing-library/vue @testing-library/dom jsdom
vite.config.js
(또는 vite.config.ts
)에서 vitest
를 구성합니다.
// vite.config.ts import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'jsdom', globals: true, setupFiles: './setupTests.ts', }, })
setupTests.ts
(선택 사항)를 만듭니다.
// setupTests.ts import '@testing-library/jest-dom/vitest';
이러한 설정을 통해 강력한 단위 테스트 환경을 갖추게 됩니다.
사용자 중심 테스트 작성
Testing Library의 핵심 아이디어는 사용자 상호 작용을 테스트하는 것입니다. Svelte 및 Vue에 대한 실용적인 예제를 탐구해 보겠습니다.
Svelte 예제: 간단한 카운터 구성 요소
간단한 Counter.svelte
구성 요소를 고려해 보겠습니다.
<!-- src/components/Counter.svelte --> <script lang="ts"> let count = 0; function increment() { count += 1; } function decrement() { count -= 1; } </script> <div> <p>Count: <span data-testid="count-value">{count}</span></p> <button on:click={decrement}>Decrement</button> <button on:click={increment}>Increment</button> </div>
이제 이 구성 요소를 테스트하는 Counter.test.ts
를 작성해 보겠습니다.
// src/components/Counter.test.ts import { render, screen, fireEvent } from '@testing-library/svelte'; import Counter from './Counter.svelte'; describe('Counter component', () => { it('should display the initial count', () => { render(Counter); const countValue = screen.getByTestId('count-value'); expect(countValue).toHaveTextContent('0'); }); it('should increment the count when the Increment button is clicked', async () => { render(Counter); const incrementButton = screen.getByRole('button', { name: /increment/i }); const countValue = screen.getByTestId('count-value'); await fireEvent.click(incrementButton); // 사용자 클릭 시뮬레이션 expect(countValue).toHaveTextContent('1'); await fireEvent.click(incrementButton); expect(countValue).toHaveTextContent('2'); }); it('should decrement the count when the Decrement button is clicked', async () => { render(Counter); const decrementButton = screen.getByRole('button', { name: /decrement/i }); const countValue = screen.getByTestId('count-value'); // 의미 있는 감소를 위해 먼저 양수로 증가 const incrementButton = screen.getByRole('button', { name: /increment/i }); await fireEvent.click(incrementButton); await fireEvent.click(incrementButton); expect(countValue).toHaveTextContent('2'); await fireEvent.click(decrementButton); expect(countValue).toHaveTextContent('1'); await fireEvent.click(decrementButton); expect(countValue).toHaveTextContent('0'); }); });
이 Svelte 예제에서는:
render(Counter)
는 구성 요소를 가상 DOM에 마운트합니다.screen.getByTestId
및screen.getByRole
은data-testid
속성 또는 접근 가능한 역할 및 이름을 기준으로 요소를 쿼리하는 데 사용되며, 사용자가 요소를 찾는 방식을 모방합니다.fireEvent.click
은 사용자가 요소를 클릭하는 것을 시뮬레이션합니다.expect(...).toHaveTextContent(...)
는 표시되는 텍스트 내용을 주장하기 위한@testing-library/jest-dom/vitest
의 매처입니다.
Vue 예제: 간단한 할 일 목록 구성 요소
이제 Vue 3 구성 요소 TodoList.vue
를 고려해 보겠습니다.
<!-- src/components/TodoList.vue --> <template> <div> <h1>Todo List</h1> <input type="text" v-model="newTask" @keyup.enter="addTodo" placeholder="Add a new todo" /> <button @click="addTodo">Add Todo</button> <ul> <li v-for="(todo, index) in todos" :key="index"> {{ todo }} <button @click="removeTodo(index)">Remove</button> </li> </ul> </div> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; export default defineComponent({ name: 'TodoList', setup() { const newTask = ref(''); const todos = ref<string[]>([]); function addTodo() { if (newTask.value.trim()) { todos.value.push(newTask.value.trim()); newTask.value = ''; } } function removeTodo(index: number) { todos.value.splice(index, 1); } return { newTask, todos, addTodo, removeTodo, }; }, }); </script>
이에 해당하는 테스트 TodoList.test.ts
는 다음과 같습니다.
// src/components/TodoList.test.ts import { render, screen, fireEvent } from '@testing-library/vue'; import TodoList from './TodoList.vue'; describe('TodoList component', () => { it('should display the title', () => { render(TodoList); expect(screen.getByText('Todo List')).toBeInTheDocument(); }); it('should add a new todo when the Add Todo button is clicked', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); const addButton = screen.getByRole('button', { name: /add todo/i }); // 입력 필드에 입력 await fireEvent.update(input, 'Learn testing'); // 버튼 클릭 await fireEvent.click(addButton); // 새 할 일이 표시되는지 확인 expect(screen.getByText('Learn testing')).toBeInTheDocument(); // 입력 필드가 지워졌는지 확인 expect(input).toHaveValue(''); }); it('should remove a todo when its Remove button is clicked', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); const addButton = screen.getByRole('button', { name: /add todo/i }); // 먼저 할 일 추가 await fireEvent.update(input, 'Buy groceries'); await fireEvent.click(addButton); expect(screen.getByText('Buy groceries')).toBeInTheDocument(); const removeButton = screen.getByRole('button', { name: /remove/i }); // 첫 번째 제거 버튼 가져오기 await fireEvent.click(removeButton); // 할 일이 문서에 더 이상 없는지 확인 expect(screen.queryByText('Buy groceries')).not.toBeInTheDocument(); }); it('should add a new todo when pressing Enter in the input field', async () => { render(TodoList); const input = screen.getByPlaceholderText('Add a new todo'); await fireEvent.update(input, 'Finish blog post'); await fireEvent.keyUp(input, { key: 'Enter', code: 'Enter' }); // Enter 키 누름 시뮬레이션 expect(screen.getByText('Finish blog post')).toBeInTheDocument(); expect(input).toHaveValue(''); }); });
이 Vue 예제에서는:
render(TodoList)
는 구성 요소를 마운트합니다.screen.getByPlaceholderText
는 입력 필드를 찾는 데 사용됩니다.fireEvent.update
는 입력 필드에 입력하는 것을 시뮬레이션합니다(값 변경).fireEvent.keyUp
은 키 누름을 시뮬레이션합니다.expect(...).toBeInTheDocument maneiras
및expect(...).not.toBeInTheDocument maneiras
는 요소 존재를 주장하는 데 사용됩니다.screen.queryByText maneiras
는getByText maneira
가 찾지 못하면 오류를 발생시키기 때문에 요소가 존재하지 않을 것으로 예상될 때 사용됩니다.
이점 및 모범 사례
Vitest와 Testing Library를 함께 사용하면 상당한 이점을 얻을 수 있습니다.
- 속도: Vitest의 HMR 및 네이티브 ESM 지원은 매우 빠른 테스트 실행으로 이어져 개발자 피드백 루프를 개선합니다.
- 사용자 중심 테스트: Testing Library는 사용자가 UI와 상호 작용하는 방식을 반영하는 테스트를 작성하도록 권장하므로 구현 변경으로 인해 덜 깨지는 더 강력한 테스트를 작성할 수 있습니다.
- 접근성 인식: 접근 가능한 역할 및 텍스트별로 쿼리하면 더 접근 가능한 애플리케이션을 구축하는 데 도움이 됩니다.
- 프레임워크에 구애받지 않는 원칙: 특정
render
함수는 다르지만 핵심 Testing Library 쿼리 및 상호 작용 패턴은 Svelte, Vue, React 및 Angular 전반에 걸쳐 일관성이 있어 프레임워크를 전환하거나 여러 프로젝트를 유지 관리하는 것이 더 쉬워집니다.
모범 사례:
- 구현이 아닌 동작 테스트: 구성 요소의 내부 상태 또는 메서드 호출이 아닌, 구성 요소가 무엇을 하는지와 사용자가 인식하는 방식에 집중합니다.
- 접근 가능한 쿼리 사용:
getByRole
,getByLabelText
,getByPlaceholderText
,getByText
를 우선적으로 사용합니다. 다른 접근 가능한 쿼리가 적합하지 않은 경우 마지막 수단으로getByTestId
를 사용합니다. - 테스트 후 정리: Testing Library는 종종 이를 처리하지만, 테스트 간에 전역 상태가 유출되지 않도록 합니다.
- 테스트를 작고 집중적으로 유지: 각 테스트는 이상적으로 단일 특정 동작을 확인해야 합니다.
- 자주 테스트 실행: 테스트를 일상 작업 흐름 및 잠재적으로 CI/CD 파이프라인에 통합합니다.
결론
Svelte 또는 Vue 프로젝트에 Vitest 및 Testing Library를 채택하는 것은 단위 테스트에 대한 강력하고 실용적인 접근 방식을 제공합니다. 사용자 중심 상호 작용에 집중하고 최신의 빠른 테스트 러너를 활용함으로써 더 안정적이고 유지 관리하기 쉬운 테스트를 작성하고 애플리케이션 품질에 대한 확신을 심어줄 수 있습니다. 이 조합은 Svelte 및 Vue 애플리케이션이 잘 작동할 뿐만 아니라 실제 사용을 견딜 수 있도록 개발 프로세스를 간소화합니다. 이러한 도구를 채택하여 더 강력하고 사용자 친화적인 프론트엔드 경험을 구축하십시오.