Testing/Vitest addon

김동현·2026년 3월 22일

Vitest addon

스토리북의 Vitest 애드온을 사용하면 컴포넌트를 스토리북 안에서 바로 테스트할 수 있습니다. 기본적으로 이 애드온은 여러분이 작성한 스토리(stories)들을 진짜 브라우저 환경에서 렌더링과 동작을 검증하는 '컴포넌트 테스트(component tests)'로 탈바꿈시켜 줍니다. 뿐만 아니라, 스토리들이 프로젝트 코드를 얼마나 잘 검증하고 있는지 나타내는 테스트 커버리지(test coverage)도 계산할 수 있죠.

만약 프로젝트에서 시각적 테스트 애드온(Visual tests addon)이나 접근성 애드온(Accessibility addon) 같은 다른 테스팅 애드온을 사용 중이시라면, 컴포넌트 테스트를 돌릴 때 이런 부가적인 테스트들도 한 번에 나란히 실행할 수 있습니다.

스토리에 대한 컴포넌트 테스트가 실행되면 그 결과(상태)가 사이드바에 즉각적으로 표시됩니다. 사이드바에서 실패한 스토리들만 골라볼 수도 있고, 실패한 스토리 옆의 메뉴 버튼을 눌러 디버깅 옵션을 확인할 수도 있답니다.

또한 '관찰 모드(watch mode)'를 켜두면, 컴포넌트나 스토리 코드를 수정할 때마다 알아서 테스트가 다시 돌아갑니다. 테스팅 위젯(testing widget)에 있는 눈 모양 아이콘(eye icon)을 누르면 관찰 모드가 켜집니다.

설치 및 설정 (Install and set up)

설치하기 전에 프로젝트가 다음 요구사항을 충족하는지 꼭 확인해 주세요:

  • Vite를 사용하는 스토리북 프레임워크 (예: vue3-vite, react-vite, preact-vite, nextjs-vite, sveltekit 등)
  • Vitest 3.0 이상
    • 만약 아직 프로젝트에 Vitest를 설치하지 않으셨더라도 걱정 마세요. 애드온 설치 시 자동으로 설치되고 설정됩니다!
  • (선택 사항) MSW 2.0 이상
    • 만약 프로젝트에 이미 MSW(Mock Service Worker)가 설치되어 있다면, Vitest의 의존성과 충돌하지 않도록 반드시 v2.0.0 이상의 버전을 사용해야 합니다.

ℹ️ Next.js 사용자를 위한 안내 — Vitest 애드온은 Next.js 14.1 이상을 지원하지만, 반드시 @storybook/nextjs-vite 프레임워크를 사용하고 있어야 합니다. 아래의 자동 설정 명령어를 실행할 때, 만약 해당 프레임워크를 사용 중이 아니라면 설치하고 변경할지 묻는 프롬프트가 나타날 거예요.

자동 설정 (Automatic setup)

아래 명령어를 터미널에 입력하면 애드온 설치부터 설정까지 한 방에 끝납니다:

npx storybook add @storybook/addon-vitest

add 명령어가 해주는 일은 다음과 같습니다:

  • Vitest 애드온 패키지를 설치하고 스토리북에 등록합니다.
  • 프로젝트의 기존 Vite와 Vitest 설정을 꼼꼼히 살펴봅니다.
  • 만약 Vitest가 없다면 설치하고, 최적의 기본값으로 세팅해 줍니다.
  • Playwright의 Chromium 브라우저를 사용하도록 브라우저 모드(browser mode)를 설정합니다.
  • (필요한 경우) Playwright 브라우저 바이너리를 설치할지 묻는 프롬프트를 띄웁니다.

이 모든 과정이 자동으로 이루어지며, 여러분을 대신해 복잡한 설정을 처리해 줍니다. 더 세밀한 설정이 필요하다면 아래의 API 섹션을 확인해 보세요.

ℹ️ 설치 중에 Playwright 브라우저 바이너리를 설치할 건지 묻는다면 "Yes"를 선택해 주세요. 컴포넌트 테스팅에 가장 권장되는 '브라우저 모드'로 테스트를 실행하려면 이 바이너리들이 꼭 필요하거든요! 나중에 따로 설치하고 싶으시다면 터미널에서 playwright install 명령어를 실행하시면 됩니다.

수동 설정 (고급) (Manual setup (advanced))

수동 설정 가이드 (자동 설정이 실패했을 때만 진행하세요)

간혹 프로젝트 설정이 독특해서 add 명령어가 애드온과 플러그인 세팅을 완벽하게 자동화하지 못하고, 몇 가지 작업을 직접 해달라고 요청할 수도 있습니다. 그럴 땐 이렇게 하시면 됩니다:

  1. 프로젝트에 Vite와 Vitest가 제대로 설정되어 있는지 확인합니다.
  2. Vitest가 브라우저 모드(browser mode)를 사용하도록 설정합니다.
  3. 프로젝트에 @storybook/addon-vitest 패키지를 설치하고, 스토리북 설정 파일(.storybook/main.js|ts)에 등록합니다.
  4. Vitest 설정 파일을 열어 스토리북 플러그인을 추가합니다. 아래의 설정 파일 예시들을 참고하시면 좋아요.
    • 만약 프로젝트에 이미 Vitest 테스트 코드들이 존재한다면, 스토리북 테스트와 기존 테스트들의 설정을 깔끔하게 분리하는 것이 좋습니다. Vitest 4.0 이상을 쓴다면 별도의 테스트 프로젝트(test project)를, Vitest 3.x를 쓴다면 워크스페이스 파일(workspace file)을 사용하는 것을 강력히 추천합니다. 이렇게 하면 필요에 따라 테스트들을 각각 따로 돌리거나, 아니면 한꺼번에 돌릴 수 있거든요.

설정 파일 예시 (Example configuration files)

add 명령어를 통한 자동 설정을 마치면 Vitest 설정 파일들이 알맞게 생성되거나 수정되었을 텐데요. 혹시 수동으로 설정을 만져야 하거나 참고할 만한 예시가 필요하시다면 아래 코드들을 살펴봐 주세요.

Vitest 설정 파일 예시 (단일 프로젝트)

플러그인을 적용하는 가장 심플한 방법은 vitest.config.ts 파일에 바로 추가하는 것입니다:

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
 
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
 
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname = path.dirname(fileURLToPath(import.meta.url));
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    test: {
      // Vitest 3.2 미만 버전에서는 `workspace` 기능을 사용하세요
      projects: [
        {
          extends: true,
          plugins: [
            storybookTest({
              // 스토리북 설정 폴더(.storybook)의 위치입니다.
              configDir: path.join(dirname, '.storybook'),
              // package.json에 정의된 스토리북 실행 스크립트 이름과 같아야 합니다.
              // --no-open 플래그를 주면 테스트 중에 브라우저 창이 자동으로 열리는 걸 막아줍니다.
              storybookScript: 'yarn storybook --no-open',
            }),
          ],
          test: {
            name: 'storybook',
            // 브라우저 모드 활성화
            browser: {
              enabled: true,
              // Playwright가 설치되어 있어야 합니다.
              provider: playwright({}),
              headless: true,
              instances: [{ browser: 'chromium' }],
            },
            setupFiles: ['./.storybook/vitest.setup.ts'],
          },
        },
      ],
    },
  }),
);

Vitest 워크스페이스 파일 예시 (Vitest < 3.2 전용)

만약 Vitest 워크스페이스(workspace)를 사용 중이시라면, 워크스페이스 파일(vitest.workspace.ts)에 새로운 워크스페이스 프로젝트를 하나 정의해주면 됩니다:

// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname = path.dirname(fileURLToPath(import.meta.url));
 
export default defineWorkspace([
  // 기존에 사용하시던 Vitest 설정 파일의 경로입니다.
  './vitest.config.ts',
  {
    // 기존에 사용하시던 Vite 설정 파일의 경로입니다.
    extends: './vite.config.ts',
    plugins: [
      storybookTest({
        // 스토리북 설정 폴더(.storybook)의 위치입니다.
        configDir: path.join(dirname, '.storybook'),
        // package.json에 정의된 스토리북 실행 스크립트 이름과 같아야 합니다.
        // --ci 플래그를 주면 프롬프트 입력을 건너뛰고 브라우저를 띄우지 않습니다.
        storybookScript: 'yarn storybook --ci',
      }),
    ],
    test: {
      name: 'storybook',
      // 브라우저 모드 활성화
      browser: {
        enabled: true,
        // Playwright가 설치되어 있어야 합니다.
        provider: 'playwright',
        headless: true,
        instances: [{ browser: 'chromium' }],
      },
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  },
]);

사용 방법 (Usage)

이 애드온을 통해 테스트를 실행하는 방법은 여러 가지가 있습니다.

참고로, 스토리북은 Playwright의 Chromium 브라우저를 사용하는 브라우저 모드(browser mode)에서 Vitest를 실행하는 것을 권장하고 있으며, 기본적으로 그렇게 설정해 드립니다. 브라우저 모드는 JSDom이나 HappyDom 같은 흉내 내기(simulations) 방식보다 훨씬 정확한 진짜 브라우저 환경에서 컴포넌트를 테스트할 수 있게 해 주거든요. 특히 브라우저 API나 특정 기능에 의존하는 컴포넌트를 테스트할 때 이건 정말 중요합니다.

스토리북 UI (Storybook UI)

가장 편한 방법은 역시 스토리북 화면(UI) 안에서 버튼을 누르는 겁니다. 클릭 한 번이면 프로젝트 전체 스토리, 특정 그룹의 스토리, 혹은 스토리 딱 하나에 대해서도 여러 가지 타입의 테스트를 촤르륵 실행할 수 있어요.

프로젝트 전체 테스트를 한 번에 돌리려면 사이드바 맨 아래에 있는 테스팅 위젯(testing widget)에서 'Run tests' 버튼을 꾹 누르시면 됩니다.

Screenshot of testing widget, expanded, with the Run tests button highlighted

위젯을 위로 쓱 펼쳐보면 컴포넌트 테스트 하위에 여러 테스트 타입들이 나열되어 있는데, 원하는 것들만 쏙쏙 골라서 켤 수도 있습니다. 관찰 모드(눈 모양 아이콘)가 켜져 있으면, 코드를 수정할 때마다 여기서 선택된 테스트들이 알아서 재실행될 거예요.

Screenshot of testing widget, expanded, showing test types and watch mode toggle

ℹ️ 혹시 시각적 테스트 애드온(Visual tests addon)을 설치하셨나요? 그렇다면 컴포넌트 테스트와 함께 시각적 테스트(Visual tests)를 동시에 실행할 수 있는 옵션이 위젯에 짠! 하고 나타납니다.

Screenshot of testing widget, expanded, showing Visual tests

접근성(a11y) 애드온 같은 다른 유용한 애드온들도 이 테스팅 위젯에 자신만의 테스트 옵션을 추가할 수 있고, 그 결과가 스토리나 컴포넌트 옆의 상태 표시기(status indicators)에 반영된답니다.

특정 스토리나 그룹만 딱 집어서 테스트하고 싶다면, 사이드바에서 해당 스토리 이름 위에 마우스를 올렸을 때 나타나는 점 세 개짜리 메뉴 버튼을 눌러보세요. 거기서 실행하고 싶은 테스트 타입을 고르시면 됩니다.

Screenshot of story sidebar item with open menu

테스트가 끝나면 사이드바에 있는 스토리와 컴포넌트들 옆에 통과(pass), 실패(fail), 에러(error)를 나타내는 알록달록한 상태 표시기가 생깁니다. 스토리 이름에 마우스를 올리고 메뉴 버튼을 누르면 해당 스토리의 구체적인 테스트 결과들을 볼 수 있어요. 거기서 결과를 하나 클릭하면 그 스토리로 바로 이동하면서, 원인을 파악할 수 있는 유용한 디버깅 패널이 아래에 열립니다. 예를 들어 인터랙션(interaction) 테스트가 실패했다면 'Interactions' 패널이 열리면서 에러가 난 부분을 바로 짚어주죠. 이 패널은 각 행동이나 단언(assertion) 단계를 하나씩 밟아가며 확인해 볼 수 있는 인터랙티브한 디버거(debugger) 역할을 톡톡히 합니다.

테스팅 위젯 하단에는 총 몇 개의 테스트가 실행되었고, 몇 개가 통과했으며, 몇 개가 실패/에러가 났는지 요약된 수치가 보여요. 실패한 숫자(failure number)를 클릭하면 사이드바에서 실패한 녀석들만 남겨두고 깔끔하게 필터링해 줍니다.

CLI

터미널에서 Vitest CLI 명령어를 쳐서 테스트를 실행할 수도 있어요. 매번 길게 치기 귀찮으니 package.json 파일에 짧은 스크립트 명령어를 하나 만들어 두는 걸 추천합니다.

// package.json
{ 
  "scripts": {
    "test-storybook": "vitest --project=storybook"
  }
}

이 스크립트는 vitest CLI 명령어를 부르면서, 대상을 Vitest 설정에 등록된 "storybook" 프로젝트로만 딱 제한(--project=storybook)합니다. (만약 위에서 설정할 때 프로젝트 이름을 다르게 지으셨다면 스크립트도 똑같이 맞춰주셔야 해요!) 필요하다면 여기에 vitest CLI 옵션들을 마음껏 더 붙여서 사용할 수 있습니다.

이제 터미널에서 아래 명령어를 탕! 치면 테스트가 (기본적으로 관찰 모드(watch mode)로) 실행됩니다:

npm run test-storybook

디버깅 (Debugging)

이 플러그인으로 테스트를 돌릴 때는 스토리북 서버가 꼭 켜져 있어야 하는 건 아니에요. 하지만 테스트가 왜 실패했는지 브라우저에서 눈으로 보며 디버깅하고 싶을 때가 많잖아요? 그럴 땐 플러그인 설정에 storybookScript 옵션을 채워 넣어 주세요. 이 옵션을 켜두고 Vitest를 관찰 모드(watch mode)로 실행하면, 플러그인이 여러분 대신 스크립트를 실행해서 스토리북 서버를 띄워줍니다! 그리고 테스트가 실패하면 터미널 결과창에 해당 스토리로 바로 갈 수 있는 클릭 가능한 링크를 친절하게 띄워주죠. 링크를 꾹 누르면 스토리북이 열리면서 문제의 원인을 금방 파악할 수 있습니다.

만약 관찰 모드를 안 쓰고 단발성으로 테스트를 돌릴 때 실패 링크를 보고 싶거나, CI 환경처럼(running tests in CI) 애초에 스토리북 서버를 띄워두고 진행하는 상황이라면 플러그인 설정에 storybookUrl 옵션을 지정해 주시면 됩니다.

Screenshot of test failure in the console, showing a failure with a link to the story

에디터 확장 프로그램 (Editor extension)

플러그인을 통해 스토리를 Vitest 테스트로 변신시키면, Vitest가 자랑하는 멋진 IDE 통합 기능(IDE integrations)의 혜택을 고스란히 누릴 수 있게 됩니다! VSCode나 JetBrains 같은 친숙한 에디터 안에서 테스트를 바로 실행하고 디버깅할 수 있게 되는 거죠.

아래 화면은 VSCode에서 Vitest 확장 프로그램(Vitest extension)을 사용해서 테스트를 돌리는 모습입니다. 스토리 코드 줄 옆에 바로 테스트 통과/실패 상태가 표시되고, 만약 실패하면 디버깅하기 좋게 해당 스토리로 넘어갈 수 있는 링크까지 친절하게 제공해 줍니다.

Screenshot of test failure in VSCode, showing a failure attached to a story

CI 환경에서 (In CI)

CI(지속적 통합) 환경에서 스토리북 테스트를 돌리는 건 기본적으로 CLI를 사용해서(via the CLI) 실행하는 방식과 거의 똑같습니다.

다만 한 가지, CI에서 테스트가 실패했을 때 터미널 출력 로그에 여러분이 예쁘게 배포해 둔 스토리북으로 바로 갈 수 있는 링크를 남겨주고 싶다면, 플러그인 설정에 storybookUrl 옵션을 꼭 적어주셔야 합니다. 구체적으로 어떻게 세팅하는지 궁금하시다면 CI에서 테스트하기 가이드의 'CI에서 실패한 테스트 디버깅하기' 섹션에 있는 상세한 예제를 참고해 주세요!

작동 원리 (How it works)

Vitest 애드온은 Vitest 플러그인을 사용하여 portable stories(포터블 스토리) 기술로 여러분의 스토리들을 Vitest 테스트로 변환해 줍니다. 또한 Vitest가 Playwright의 Chromium 브라우저를 사용하는 브라우저 모드(browser mode)에서 테스트를 실행하도록 알아서 설정해 주죠. 이 애드온은 Vitest 위에서 동작하기 때문에 반드시 Vite를 사용하는 스토리북 프레임워크가 필요합니다.

스토리 테스트는 두 가지 방식으로 이루어집니다. 기본적으로 컴포넌트가 화면에 잘 그려지는지 확인하는 스모크 테스트(smoke test)가 실행되고, 만약 스토리에 play 함수(play function)가 정의되어 있다면 해당 함수를 실행하여 그 안에 있는 단언(assertions)들까지 완벽하게 검증합니다.

스토리북 UI(Storybook UI)에서 테스트를 실행하면, 애드온이 백그라운드에서 Vitest를 돌리고 그 결과를 사이드바에 즉각적으로 보여주는 원리입니다.

테스트 설정 (Configuring tests)

애드온이 실행하는 테스트는 크게 두 가지 방향으로 설정할 수 있어요. 실행할 테스트 타입(종류)을 켜고 끌 수도 있고, 어떤 스토리를 테스트에 포함할지(include), 뺄지(exclude), 아니면 결과만 확인할지(skip) 지정할 수도 있습니다.

테스트 타입 켜고 끄기 (Toggling test types)

여러분이 프로젝트에 어떤 애드온을 설치했느냐에 따라, Vitest 애드온은 컴포넌트 테스트 외에도 여러 종류의 테스트를 지원합니다. 시각적 테스트(visual tests)처럼 완전히 독립적으로 실행되는 테스트도 있고, 접근성(accessibility)처럼 컴포넌트 테스트와 반드시 함께 돌아가야 하는 테스트도 있죠. 컴포넌트 테스트와 함께 돌아가는 테스트들은 테스팅 위젯(testing widget) 안에서 체크박스를 껐다 켰다 하면서 자유롭게 설정할 수 있습니다.

Screenshot of testing widget, expanded, everything is checked

(참고로 설치된 애드온에 따라 위 이미지에 나오는 모든 테스트 타입이 보이지 않을 수도 있습니다!)

테스트 포함(Include), 제외(Exclude), 건너뛰기(Skip) (Including, excluding, or skipping tests)

태그(tags)를 사용하면 스토리를 테스트 대상에 넣거나, 아예 빼버리거나, 아니면 실행만 건너뛸 수 있습니다. 포함된(Included) 스토리는 정상적으로 테스트되고, 제외된(Excluded) 스토리는 테스트도 안 하고 결과 통계에도 안 잡힙니다. 반면 건너뛴(Skipped) 스토리는 테스트를 실행하진 않지만 테스트 결과 통계에는 '건너뜀' 상태로 꼼꼼히 기록돼요.

플러그인은 기본적으로 test 태그가 붙은 모든 스토리를 실행하도록 설정되어 있습니다. 플러그인 설정 옵션에 tags 옵션을 추가하면 이 기본 동작을 바꿀 수 있어요. 태그를 기반으로 스토리를 쏙쏙 골라낼 수 있는 거죠.

예를 들어, Button 컴포넌트의 모든 스토리에 stable 태그를 달아주고, 아직 개발 중인 ExperimentalFeatureStory 하나에만 experimental 태그를 달아볼까요?

// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, nextjs-vite, etc.
import type { Meta, StoryObj } from '@storybook/your-framework';
 
import { Button } from './Button';
 
const meta = {
  component: Button,
  // 👇 이 파일 안의 모든 스토리에 'stable' 태그를 적용합니다.
  tags: ['stable'],
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const ExperimentalFeatureStory: Story = {
  //👇 이 스토리에 한해서 부모로부터 물려받은 `stable` 태그를 떼어내고, `experimental` 태그를 달아줍니다.
  tags: ['!stable', 'experimental'],
};

이제 이 태그들을 테스트 동작과 연결해 볼게요. 플러그인 설정에서 experimental 태그가 붙은 녀석들은 테스트에서 제외(exclude)하라고 알려주면 됩니다:

// vitest.config.ts
import { defineConfig, mergeConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
 
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
 
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname = path.dirname(fileURLToPath(import.meta.url));
 
import viteConfig from './vite.config';
 
export default mergeConfig(
  viteConfig,
  defineConfig({
    test: {
      // Use `workspace` field in Vitest < 3.2
      projects: [
        {
          extends: true,
          plugins: [
            storybookTest({
              // The location of your Storybook config, main.js|ts
              configDir: path.join(dirname, '.storybook'),
              // This should match your package.json script to run Storybook
              // The --no-open flag will skip the automatic opening of a browser
              storybookScript: 'yarn storybook --no-open',
              // 👇 바로 여기에 태그 설정을 추가합니다!
              tags: {
                include: ['test'],
                exclude: ['experimental'],
              },
            }),
          ],
          test: {
            name: 'storybook',
            // Enable browser mode
            browser: {
              enabled: true,
              // Make sure to install Playwright
              provider: playwright({}),
              headless: true,
              instances: [{ browser: 'chromium' }],
            },
            setupFiles: ['./.storybook/vitest.setup.ts'],
          },
        },
      ],
    },
  }),
);
// vitest.workspace.ts
import { defineWorkspace } from 'vitest/config';
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const dirname = path.dirname(fileURLToPath(import.meta.url));
 
export default defineWorkspace([
  // This is the path to your existing Vitest config file
  './vitest.config.ts',
  {
    // This is the path to your existing Vite config file
    extends: './vite.config.ts',
    plugins: [
      storybookTest({
        // The location of your Storybook config, main.js|ts
        configDir: path.join(dirname, '.storybook'),
        // This should match your package.json script to run Storybook
        // The --ci flag will skip prompts and not open a browser
        storybookScript: 'yarn storybook --ci',
        // 👇 바로 여기에 태그 설정을 추가합니다!
        tags: {
          include: ['test'],
          exclude: ['experimental'],
        },
      }),
    ],
    test: {
      name: 'storybook',
      // Enable browser mode
      browser: {
        enabled: true,
        // Make sure to install Playwright
        provider: 'playwright',
        headless: true,
        instances: [{ browser: 'chromium' }],
      },
      setupFiles: ['./.storybook/vitest.setup.ts'],
    },
  },
]);

만약 똑같은 태그가 include 배열과 exclude 배열에 동시에 들어간다면, 얄짤없이 exclude 설정이 우선권을 가집니다(우선순위가 더 높습니다).

테스트 러너와의 비교 (Comparison to the test runner)

기존의 테스트 러너(test runner)는 테스트를 돌리려면 반드시 스토리북이 켜져 있어야(running) 했어요. 브라우저가 각 스토리에 일일이 방문해서 play 함수를 실행하고 결과를 기다려야 했으니까요.

하지만 Vitest 플러그인은 접근 방식이 완전히 다릅니다! Vite와 포터블 스토리를 사용해서 스토리 코드를 아예 테스트 코드로 '변환'해 버리기 때문에 스토리북을 실행할 필요 자체가 없어요. 물론 Vite를 뼈대로 쓰기 때문에 Vite 기반의 스토리북 프레임워크(또는 Next.js)에서만 쓸 수 있다는 제한은 있지만요. (반면 기존 테스트 러너는 프레임워크를 가리지 않고 다 쓸 수 있죠!)

기능 (Feature)Vitest 애드온테스트 러너 (test-runner)
테스트 종류 (Test types)
- 인터랙션 테스트(Interaction tests)
- 접근성 테스트(Accessibility tests)
- 시각적 테스트(Visual tests)
- 스냅샷 테스트(Snapshot tests)
테스트 실행 환경 (Testing contexts)
- 스토리북 화면 안에서 (Storybook UI)
- 에디터 확장 프로그램 (Editor extensions)
- 터미널 (CLI)
- 지속적 통합 환경 (In CI)
실제 구동되는 도구VitestJest
모든 스토리북 프레임워크 지원 여부❌ (Vite 필수)
진짜 브라우저 환경에서 실행
코드 커버리지 계산 기능✅ (addon-coverage 필요)
스토리북 서버 실행 필수 여부
다른 애드온으로 확장 기능

테스트 러너는 오직 까만 터미널 창(CLI)에서만 쓸 수 있는 도구였어요. 테스트를 돌리기 위한 UI 화면도 없고 에디터 확장 프로그램도 없었죠. 하지만 Vitest 애드온은 스토리북 UI 안에 예쁜 버튼들을 만들어주고, Vitest의 IDE 통합 기능까지 덤으로 쓸 수 있게 해 줍니다.

거기다 테스트 러너는 백그라운드에서 Jest가 복잡하게 조종(orchestrated)하는 방식이었지만, 이 플러그인은 여러분의 스토리를 진짜 테스트 코드로 바꿔서 Vitest 위에서 깔끔하게 돌리기 때문에 구조가 훨씬 단순하고 설정하기도 편해요.

마지막으로 구조가 단순해지고 Vitest의 쾌속 엔진을 사용하는 덕분에, 대부분의 프로젝트에서 이 플러그인이 테스트 러너보다 훨씬 더 빠를 것으로 기대하고 있습니다. (나중에 정확한 벤치마크 지표를 공개할게요!)

자주 묻는 질문 (FAQ)

Vitest 자체에서 에러가 발생하면 어떻게 되나요? (What happens if Vitest itself has an error?)

가끔 테스트 코드의 문제가 아니라 Vitest 자체의 꼬임 때문에 에러가 날 때가 있어요. 이럴 땐 스토리북 UI의 테스팅 위젯이 아주 친절하게 에러가 났다고 알려주고, 링크를 클릭해서 자세한 원인을 살펴볼 수 있게 해 줍니다. 물론 브라우저 콘솔에도 에러 로그가 찍혀요.

Screenshot of testing widget, expanded, showing Vitest error

흔하게 겪는 에러들에 대한 해결책은 Vitest의 문제 해결(troubleshooting) 가이드에서 찾아볼 수 있습니다.

여러 환경에서 돌렸더니 테스트 결과가 다르게 나오면 어떡하죠? (What happens when there are different test results in multiple environments?)

이 애드온으로 테스트를 돌리면, 여러분의 프로젝트에 세팅해 둔 Vitest 설정을 그대로 따라서 테스트가 실행됩니다. 기본적으로는 Playwright의 Chromium 브라우저를 쓰는 '브라우저 모드(browser mode)'로 돌아가고요. 간혹 애드온(이나 CLI)으로 돌릴 땐 실패하던 테스트가 스토리북의 Interactions 패널에서 눈으로 볼 땐 성공하는 경우가 생길 수 있어요(혹은 그 반대거나). 이건 테스트가 실행되는 '환경' 자체가 달라서 발생하는 불가피한 동작 차이 때문이랍니다.

스토리북 안에서 CLI 테스트를 어떻게 디버깅하나요? (How do I debug my CLI tests in Storybook?)

CLI에서 테스트가 실패했을 때, 원인을 빠르게 파악할 수 있도록 플러그인이 해당 스토리로 바로 갈 수 있는 링크를 뱉어내 줍니다 (디버깅 섹션 참조).

그런데 관찰 모드(watch mode)로 돌리는데 링크가 제대로 안 먹힌다면, 다음 두 가지 설정을 확인해 보세요:

  • storybookUrl: 이 주소가 정확하고 실제로 접속 가능한지 확인하세요. (기본값은 http://localhost:6006인데, 여러분이 쓰는 포트 번호가 다를 수도 있으니까요!)
  • storybookScript: 이 스크립트가 스토리북을 제대로 실행하고 있는지 확인하세요.

만약 CI 환경에서 돌리는데 URL 링크가 안 먹힌다면, 테스트를 돌리기 전에 스토리북이 먼저 확실하게 빌드되고 배포(published)되었는지 점검해야 합니다. 그런 다음 배포된 주소를 storybookUrl 옵션에 쏙 넣어주시면 돼요. 자세한 예시는 CI 환경에서(In CI) 섹션을 참고하세요!

테스트 코드가 public 폴더 안의 에셋(이미지 등)을 잘 찾게 하려면요? (How do I ensure my tests can find assets in the public directory?)

스토리에서 프로젝트의 public 폴더 안에 있는 이미지나 에셋을 가져다 쓰는데, 만약 그 폴더 이름이 public이 아니라면 어떻게 해야 할까요? Vitest 설정 파일에서 publicDir 옵션을 사용해서 여러분이 사용하는 진짜 폴더 위치를 알려주면 됩니다.

스토리북 테스트만 완전히 분리해서 돌리고 싶어요! (How do I isolate Storybook tests from others?)

어떤 프로젝트는 Vite 설정 파일(Vite config) 안에 아예 test 속성을 정의해서 쓰고 있는 경우가 있습니다. 이 플러그인이 쓰는 Vitest 설정은 기존 Vite 설정을 상속(extends)받아서 만들어지기 때문에, 두 test 속성이 하나로 뭉쳐지면서(merged) 스토리북 테스트에 예상치 못한 문제가 생길 수 있어요.

스토리북 테스트를 외부의 간섭으로부터 완벽하게 지켜내려면, 기존 Vite 설정 파일에 있던 test 속성을 뽑아서 Vitest 설정 파일 쪽으로 옮겨주셔야 합니다. 그렇게 하면 플러그인이 마음 편히 Vite 설정을 상속받으면서도 test 속성은 섞이지 않게 분리할 수 있거든요.

더 나아가, Vitest 4.0 이상이라면 테스트 프로젝트(test project) 기능을, 이전 버전을 쓴다면 워크스페이스(workspace) 기능을 활용해서 스토리북용 설정과 일반 테스트용 설정을 깔끔하게 분리하는 것을 강력 추천합니다!

굳이 왜 '브라우저 모드'를 추천하는 건가요? (Why do we recommend browser mode?)

Vitest의 브라우저 모드를 켜면 (Playwright를 통해) 진짜 브라우저인 Chromium을 띄워서 그 안에서 테스트를 쌩쌩 돌립니다. 이 방식의 반대말은 JSDom이나 HappyDom처럼 브라우저인 '척'하는 가상 환경을 쓰는 건데요, 아무래도 흉내 내는 것이다 보니 진짜 브라우저와는 미묘하게 동작이 다를 수밖에 없습니다. 특히 브라우저만의 고유한 API나 최신 기능에 의존하는 UI 컴포넌트들을 깐깐하게 검증하려면, 가상 환경보다는 진짜 브라우저를 띄워놓고 테스트하는 게 훨씬 정확하고 안전합니다.

더 자세한 이야기는 브라우저 모드를 효과적으로 사용하는 방법에 대한 Vitest 공식 가이드를 참고해 보세요.

Playwright 말고 WebDriver를 쓰고 싶어요! (How do I use WebDriver instead of Playwright?)

저희는 Playwright를 기본으로 추천하지만, 원하신다면 WebDriverIO를 쓰셔도 전혀 문제없습니다. Vitest 설정 파일에서 브라우저 제공자(browser provider) 옵션을 입맛에 맞게 슥 바꿔주시면 됩니다.

Chromium 말고 다른 브라우저로 테스트할 순 없나요? (How do I use a browser other than Chromium?)

가장 많은 사용자들이 쓰는 환경과 비슷하게 맞추기 위해 Chromium을 1순위로 추천드리고 있지만, 당연히 다른 브라우저로도 테스트가 가능합니다! Vitest 설정 파일에서 브라우저 이름(browser name) 옵션을 살짝 바꿔주기만 하면 돼요. (단, Playwright와 WebDriverIO가 지원하는 브라우저 종류가 조금 다를 수 있으니 참고하세요.)

테스트 이름(description)을 내 맘대로 바꾸고 싶어요! (How do I customize a test name?)

기본적으로는 스토리를 내보낼 때 쓴 변수 이름(export name)이 그대로 테스트 이름이 됩니다. 만약 띄어쓰기나 괄호, 특수문자 등을 섞어서 더 그럴듯하고 친절한 테스트 설명을 적고 싶으시다면, 스토리 객체에 name 속성을 추가해 주면 끝이에요!

// Example.stories.js|ts
export const Story = {
  name: 'custom, descriptive name'
};

Error: Vitest failed to find the current suite 에러가 나면 어쩌죠? (How do I fix the Error: Vitest failed to find the current suite error?)

이 에러는 보통 Vitest 자체의 결함이라기보단 스토리가 테스트 코드로 '변환(transform)'되는 과정에서 뭔가 꼬였을 때 자주 나타납니다. 당황하지 마시고 아래 순서대로 원인을 찾아보세요:

  1. 전체 에러 로그를 천천히 읽어보세요. 특히 스토리 변환 과정 쪽에 단서가 숨어있을 확률이 높습니다.
  2. 혹시 Vite의 의존성 최적화(dependency optimization) 관련 경고 문구(예: "new dependencies optimized: lodash")가 뜨는지 눈여겨보세요.
  3. 만약 이런 경고가 뜬다면, 테스트 실행 도중에 갑자기 리로드(reload)가 발생하면서 테스트가 중간에 끊겨버릴 수 있습니다.

가장 빠르고 확실한 해결책은 의존성들을 미리 최적화(pre-optimize)해 두는 것입니다. Vite 설정 파일의 optimizeDeps.include 배열 안에 해당 패키지들을 쏙 넣어주세요. 이렇게 하면 테스트 한창 돌고 있는데 뜬금없이 의존성 최적화가 끼어들어 Vitest의 심기를 건드리는 일을 원천 차단할 수 있습니다.

CI에서 돌릴 때 "Failed to fetch dynamically imported module"이나 "Cannot connect to the iframe" 에러가 뜨는데 왜 이러는 건가요? (Why do my tests fail in CI with "Failed to fetch dynamically imported module" or "Cannot connect to the iframe"?)

이런 에러들은 주로 로컬보다는 CI 환경에서, 수많은 테스트들을 한꺼번에 동시에(simultaneously) 쏟아부을 때 발생합니다. 컴퓨터 자원이 순간적으로 벅차서 헐떡이는 거죠. 로컬 컴퓨터는 보통 자원이 넉넉하거나 동시에 돌리는 테스트 개수가 적어서 이런 꼴을 볼 일이 잘 없습니다.

이를 해결하기 위한 두 가지 특급 처방전이 있습니다:

1. 격리 모드(isolation mode) 꺼버리기

Vitest는 기본적으로 각각의 테스트 파일들을 자기만의 독립된 캡슐(environment) 안에 가둬놓고 실행합니다. 이 기능을 잠시 꺼두면 컴퓨터 자원 소모를 크게 줄일 수 있어서 저런 에러들을 예방할 수 있어요:

// vitest.config.ts
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  test: {
    // ...
    isolate: false,
  },
});

더 자세한 정보는 Vitest의 isolate 설정 문서를 읽어보시면 도움이 될 거예요.

2. 샤딩(sharding)으로 테스트를 여러 CI Job에 나누어 담기

테스트 파일들을 통째로 돌리지 말고, 여러 개의 병렬(parallel) CI Job에 잘게 쪼개서(distributes) 돌리는 기술입니다. 이렇게 하면 각각의 실행기(runner)가 받는 부담이 확 줄어들어요:

# 전체 3조각 중 첫 번째 조각 실행
vitest run --shard=1/3
 
# 전체 3조각 중 두 번째 조각 실행
vitest run --shard=2/3
 
# 전체 3조각 중 세 번째 조각 실행
vitest run --shard=3/3

이 기술을 CI 파이프라인에 어떻게 녹여낼지 궁금하시다면 성능 향상을 위한 Vitest 샤딩 가이드(Vitest's sharding guide)를 꼼꼼히 읽어보시길 권장합니다.

API

모듈 내보내기 (Exports)

이 애드온 패키지에서 가져다 쓸 수 있는 모듈은 다음과 같습니다:

import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'

storybookTest

타입: function

이 녀석은 여러분의 스토리들을 Vitest 테스트 코드로 샤샤샥 변환시켜주는 핵심 Vitest 플러그인(Vitest plugin) 함수입니다. 입맛에 맞게 설정 객체(options object)를 넣어주시면 돼요.

옵션 (Options)

storybookTest 함수는 옵션 객체를 받아서 다양한 설정을 할 수 있습니다. 사용 가능한 속성들은 다음과 같습니다:

configDir

타입: string

기본값: .storybook

현재 작업 폴더(working directory)를 기준으로 한 스토리북 설정 폴더의 상대적인 경로입니다.

만약 여러분의 스토리북 설정(Storybook configuration) 폴더 이름이 .storybook이 아니거나 루트 위치가 아닌 다른 곳에 꽁꽁 숨어있다면, 플러그인이 헤매지 않도록 반드시 이 경로를 정확하게 적어주셔야 합니다!

storybookScript

타입: string

스토리북을 띄울 때 사용할 실행 스크립트(선택 사항)입니다. 이걸 적어두면 Vitest를 관찰 모드(watch mode)로 돌릴 때, 플러그인이 이 스크립트를 사용해서 스토리북 서버를 뒤에서 살짝 띄워줍니다. (단, 아래의 storybookUrl 주소에 이미 쌩쌩 돌아가고 있는 스토리북 서버가 확인되면 스크립트를 굳이 실행하진 않습니다.)

storybookUrl

타입: string

기본값: http://localhost:6006

스토리북 서버가 돌아가고 있는 URL 주소입니다. 내부적인 테스트 검사용으로도 쓰이고, 무엇보다 테스트 실패 시 터미널 결과창에 디버깅하러 편하게 이동할 수 있는 링크(link to the story)를 찍어줄 때 사용됩니다.

tags

타입:

{
  include: string[];
  exclude: string[];
  skip: string[];
}

기본값:

{
  include: ['test'],
  exclude: [],
  skip: [],
}

테스트에 포함(include)하거나, 제외(exclude)하거나, 건너뛸(skip) 태그(Tags)들을 지정합니다. 이 태그들은 스토리, meta, 또는 preview 파일에 어노테이션(annotations) 형태로 정의됩니다.

  • include: 여기에 해당하는 태그를 가진 스토리들만 테스트 대상이 됩니다.
  • exclude: 여기에 해당하는 태그를 가진 스토리들은 아예 테스트 대상에서 제외되며, 전체 테스트 결과 통계(예: 테스트 개수)에도 집계되지 않습니다.
  • skip: 여기에 해당하는 태그를 가진 스토리들은 실제로 테스트를 돌리지는 않지만, 전체 테스트 결과 통계에는 '건너뜀(skipped)' 상태로 포함되어 계산됩니다.

disableAddonDocs

타입: boolean

기본값: true

테스트를 실행할 때 애드온 문서용(addon docs) MDX 파일 파싱 기능을 비활성화할지 여부를 결정합니다.

일반적으로 스토리북 테스트 환경에서는 속도 최적화를 위해 프리뷰 설정 파일이나 스토리 파일에서 mdx 파일을 불러올(import) 때 실제 내용을 파싱하지 않고 가짜(mock)로 처리해서 넘깁니다. 굳이 문서를 읽어올 필요가 없으니까요.

하지만 만약 여러분의 스토리나 컴포넌트가 화면을 렌더링하는 과정에서 MDX 파일 안의 내용을 반드시 읽고 분석(parse)해야만 정상적으로 작동하는 특별한 상황이라면, 이 disableAddonDocs 값을 false로 바꿔서 MDX 파싱 기능을 켜두셔야 합니다.

더 유용한 테스팅 관련 자료들

profile
프론트에_가까운_풀스택_개발자

0개의 댓글