FE
[SvelteKit] SvelteKit 테스트, 더 빠르게: Vitest로 유닛·컴포넌트 테스트 시작하기
| 가볍고 정확한 테스트로 개발 경험을 한 단계 끌어올리기
안녕하세요! 팡일입니다.
이번 글에서는 작고 빠른 단위 테스트(Unit Test)와 컴포넌트 단위 테스트(Component Test)를 책임지는 Vitest 기반의 테스트 환경을 다뤄보겠습니다. SvelteKit + Vite 조합에서는 Vitest가 사실상 “공식 스택”에 가까웠습니다.
| 왜 Vitest인가?
Vitest는 SvelteKit·Vite 프로젝트에 가장 자연스럽게 녹아드는 테스트 러너입니다
Vitest는 Vite 팀이 직접 만든 테스트 러너로, Vite 기반 프로젝트(SvelteKit 포함)를 위해 최적화되어 있습니다. 따라서 별도의 복잡한 설정이나 추가 번들링 과정 없이도 프로젝트 구조와 동일한 방식으로 모듈을 불러올 수 있고, ESM 기반 개발 흐름과도 매끄럽게 호흡합니다. 덕분에 단순한 “테스트 도구”가 아니라 개발 환경 자체의 자연스러운 확장처럼 느껴진다는 점이 큰 장점입니다.
1) 매우 빠릅니다 — 체감 속도가 다릅니다
Vitest는 브라우저를 띄우지 않고 Node 환경에서 바로 실행되기 때문에 Playwright나 Cypress와 같은 E2E 도구에 비해 속도가 비교 불가능할 정도로 빠릅니다.
파일 저장 → 테스트 자동 재실행
watch 모드에서의 깔끔한 출력
변경된 부분만 빠르게 검증
이런 흐름 덕분에 테스트가 개발의 리듬을 끊지 않습니다. 테스트가 아니라, 그냥 개발 과정의 자연스러운 일부처럼 느껴질 정도입니다.
2) 작은 단위 테스트에 집중하기 좋습니다
Vitest의 가장 큰 강점은 기능 전체가 아닌 ‘특정 부분’의 동작만 정확하게 검증하는 상황에 최적화되어 있다는 점입니다.
특정 함수가 원하는 대로 동작하는지
특정 Store 로직이 의도대로 상태를 변경하는지
특정 컴포넌트가 주어진 props에 따라 제대로 렌더링되는지
이런 식으로, 작은 조각 단위로 정확성을 확인하는 테스트가 필요할 때 Vitest는 최고의 선택지가 됩니다. E2E 테스트는 전체 사용자 플로우를 확인하는 데는 좋지만, 작은 단위를 고립해서 테스트하기에는 오버엔지니어링이 되기 쉽습니다. Vitest는 반대로 “이 부분만 정확하게 돌아가는지 알고 싶다”라는 요구에 정확히 맞춰진 도구입니다.
3) 개발 흐름을 방해하지 않습니다 — 테스트가 자연스럽게 곁에서 함께 움직입니다
Vitest의 watch 모드를 켜두면 개발과 테스트가 거의 동시에 흘러갑니다.
코드를 수정하면 해당 부분의 테스트만 즉시 재실행
실패한 테스트만 따로 확인 가능
통과 여부도 바로 확인
특히 SvelteKit과 함께 사용할 때는 Store, Utility 함수, API 호출 로직 같은 부분들을 아주 쉽게 고립시켜 테스트하실 수 있습니다. 이런 흐름은 단순 편리함을 넘어서 컴포넌트 품질과 리팩터링 속도 자체를 확실히 끌어올려 줍니다.
| 가장 기본: 순수 함수(Unit Test) 테스트하기
우선 간단한 예제로 설명해보겠습니다.
1) 먼저, 아래의 코드를 작성합니다.
// src/utils/math.ts
export function sum(a: number, b: number): number {
return a + b;
}// src/utils/math.test.ts
import { describe, it, expect } from 'vitest';
import { sum } from './math';
describe('sum 함수', () => {
it('두 숫자를 더한 값을 올바르게 반환한다', () => {
expect(sum(1, 2)).toBe(3);
});
it('음수를 포함한 덧셈도 올바르게 처리한다', () => {
expect(sum(-1, 5)).toBe(4);
});
});2) 테스트 명령어를 실행합니다.
npm run test:unit진짜 눈 깜짝할 사이에 끝납니다. 유닛 테스트의 가장 큰 매력은 바로 이 민첩함입니다.
| 실전: Svelte 컴포넌트 테스트(Component Test)
SvelteKit에서도 Vitest + Testing Library를 사용하면 “사용자 관점의 UI 렌더링 테스트”가 가능합니다.
하지만 저는 처음 시도했을 때 다음과 같은 오류를 마주했습니다:
Svelte error: lifecycle_function_unavailable
`mount(...)` is not available on the server원인은 Vitest가 Svelte 컴포넌트를 SSR(서버 렌더링) 모드로 불러왔기 때문입니다. Testing Library는 DOM 환경(jsdom)에서 동작해야 하는데 SSR 전용 코드가 실행되면서 충돌이 난 것이죠.
이 문제는 아래 설정 한 줄로 해결됐습니다:
resolve: {
conditions: ['browser']
}이 설정이 Vitest로 하여금 Svelte 컴포넌트의 ‘브라우저 전용(CSR)’ 버전만 불러오도록 강제합니다. 그리고 이 순간부터 Testing Library 컴포넌트 테스트가 정상 동작하기 시작했습니다.
1) 실제 동작했던 컴포넌트 테스트
예제로 StartButton.svelte라고 해보겠습니다.
// src/lib/components/common/StartButton.svelte
<script lang="ts">
import { goToLogin } from '$lib/utils/navigation';
</script>
<button onclick={goToLogin} class="...">
시작하기
</button>// src/tests/vitest/StartButton.test.ts
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
vi.mock('$lib/utils/navigation', () => ({
goToLogin: vi.fn()
}));
// mock 이후 import
import { goToLogin } from '$lib/utils/navigation';
import StartButton from '$lib/components/common/StartButton.svelte';
describe('StartButton 컴포넌트', () => {
it('"시작하기" 텍스트를 가진 버튼을 렌더링한다', () => {
render(StartButton);
const button = screen.getByRole('button', { name: '시작하기' });
expect(button).toBeInTheDocument();
});
it('버튼 클릭 시 goToLogin 함수가 호출된다', async () => {
render(StartButton);
const button = screen.getByRole('button', { name: '시작하기' });
await fireEvent.click(button);
expect(goToLogin).toHaveBeenCalledTimes(1);
});
});2) 이 테스트로 검증할 수 있는 것들
버튼이 제대로 렌더링되는가?
클릭 시 의도한 함수가 정확히 호출되는가?
네비게이션, 네트워크, 실제 라우팅 등은 실행되지 않는가?
즉, 컴포넌트가 맡은 단일 역할을 “정확하게” 수행하는지 확인하는 테스트입니다.
| 마치며
Vitest는 SvelteKit 개발자에게 정말 강력한 도구입니다. 빠르고, 가볍고, 로직 단위 테스트에 최적화되어 있고, Testing Library와 함께라면 컴포넌트 테스트도 가능합니다.
저는 컴포넌트 테스트가 처음에는 잘 동작하지 않아 애를 먹었지만 resolve.conditions = ['browser'] 한 줄로 SvelteKit의 SSR 문제를 해결한 뒤부터는 테스트가 개발 과정에 자연스럽게 녹아드는 경험을 할 수 있었습니다.
앞으로 SvelteKit 프로젝트를 유지보수하거나 기능을 확장할 때 Vitest 기반의 테스트 환경은 확실히 든든한 기반이 되어줄 것입니다.