테스트 없는 코드는 "동작하는 것 같은" 코드입니다 — 테스트가 있어야 "동작하는" 코드입니다.

개념 정의

컴포넌트 테스트 는 Svelte 컴포넌트가 올바르게 렌더링되고, 사용자 인터랙션에 적절히 반응하는지 검증합니다. Vitest(테스트 러너) + Svelte Testing Library(렌더링/쿼리) 조합이 표준입니다.

설정

BASH
npm install -D vitest @testing-library/svelte @testing-library/jest-dom jsdom @sveltejs/vite-plugin-svelte
JAVASCRIPT
// vite.config.js
import { defineConfig } from 'vitest/config';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [svelte({ hot: !process.env.VITEST })],
  test: {
    include: ['src/**/*.{test,spec}.{js,ts}'],
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./src/tests/setup.js'],
  },
});
JAVASCRIPT
// src/tests/setup.js
import '@testing-library/jest-dom/vitest';

기본 테스트

SVELTE
<!-- Counter.svelte -->
<script>
  let count = $state(0);
</script>

<button onclick={() => count++}>카운트: {count}</button>
JAVASCRIPT
// Counter.test.js
import { render, screen, fireEvent } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import Counter from './Counter.svelte';

describe('Counter', () => {
  it('초기 카운트는 0이어야 한다', () => {
    render(Counter);
    expect(screen.getByRole('button')).toHaveTextContent('카운트: 0');
  });

  it('클릭하면 카운트가 증가해야 한다', async () => {
    render(Counter);
    const button = screen.getByRole('button');

    await fireEvent.click(button);
    expect(button).toHaveTextContent('카운트: 1');

    await fireEvent.click(button);
    expect(button).toHaveTextContent('카운트: 2');
  });
});

Props 테스트

SVELTE
<!-- Greeting.svelte -->
<script>
  let { name, greeting = '안녕하세요' } = $props();
</script>

<h1>{greeting}, {name}님!</h1>
JAVASCRIPT
import { render, screen } from '@testing-library/svelte';
import Greeting from './Greeting.svelte';

describe('Greeting', () => {
  it('이름과 기본 인사말을 표시해야 한다', () => {
    render(Greeting, { props: { name: '홍길동' } });
    expect(screen.getByRole('heading')).toHaveTextContent('안녕하세요, 홍길동님!');
  });

  it('커스텀 인사말을 표시해야 한다', () => {
    render(Greeting, { props: { name: '김철수', greeting: '반갑습니다' } });
    expect(screen.getByRole('heading')).toHaveTextContent('반갑습니다, 김철수님!');
  });
});

사용자 인터랙션 테스트

JAVASCRIPT
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import SearchBar from './SearchBar.svelte';

describe('SearchBar', () => {
  it('입력 후 엔터를 누르면 검색 콜백이 호출되어야 한다', async () => {
    const user = userEvent.setup();
    const onSearch = vi.fn();

    render(SearchBar, { props: { onSearch } });

    const input = screen.getByPlaceholderText('검색어 입력');
    await user.type(input, 'Svelte');
    await user.keyboard('{Enter}');

    expect(onSearch).toHaveBeenCalledWith('Svelte');
  });
});

비동기 테스트

JAVASCRIPT
import { render, screen, waitFor } from '@testing-library/svelte';
import UserList from './UserList.svelte';

// fetch 모킹
beforeEach(() => {
  global.fetch = vi.fn().mockResolvedValue({
    ok: true,
    json: () => Promise.resolve([
      { id: '1', name: '홍길동' },
      { id: '2', name: '김철수' },
    ]),
  });
});

describe('UserList', () => {
  it('사용자 목록을 로드하여 표시해야 한다', async () => {
    render(UserList);

    // 로딩 상태 확인
    expect(screen.getByText('로딩 중...')).toBeInTheDocument();

    // 데이터 로드 후 확인
    await waitFor(() => {
      expect(screen.getByText('홍길동')).toBeInTheDocument();
      expect(screen.getByText('김철수')).toBeInTheDocument();
    });
  });
});

접근성 테스트

JAVASCRIPT
import { render } from '@testing-library/svelte';
import { axe, toHaveNoViolations } from 'jest-axe';
import LoginForm from './LoginForm.svelte';

expect.extend(toHaveNoViolations);

it('접근성 위반이 없어야 한다', async () => {
  const { container } = render(LoginForm);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

면접 포인트

  • "컴포넌트 테스트에서 가장 중요한 원칙은?": 구현 세부사항이 아닌 사용자 관점에서 테스트합니다. 내부 상태를 직접 확인하는 대신, 화면에 보이는 텍스트나 역할(role)을 기준으로 쿼리합니다.
  • "Testing Library의 쿼리 우선순위는?": getByRole > getByLabelText > getByPlaceholderText > getByText > getByTestId. 접근성이 좋은 쿼리를 우선 사용합니다.

정리

Svelte 컴포넌트 테스트는 "사용자가 보는 것을 테스트한다"가 핵심입니다. Vitest로 빠르게 실행하고, Testing Library로 사용자 관점에서 검증하면, 리팩토링에도 깨지지 않는 견고한 테스트를 작성할 수 있습니다.

댓글 로딩 중...