C.W.K.
Stream
Lesson 01 of 03 · published

v8 로 Coverage — 중요한 거 측정

~16 min · vitest-advanced, coverage, v8

Level 0테스트 호기심
0 XP0/32 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Coverage 는 네가 안 테스트한 거 말해줘. 잘 테스트했는지는 말 못 해."

왜 default 로 v8 (Istanbul 말고)

Vitest 는 두 coverage provider 지원: v8 (V8 엔진 native) 와 istanbul (소스 instrumentation). v8 가 default 고 보통 맞는 선택 — 더 빠르고 (소스 재작성 없음), ESM/async 코드에 더 정확하고, TypeScript / Vite 플러그인 춤 없이 작동. Istanbul 은 특정 instrumentation 기능 (예: 여러 런타임 걸친 coverage 병합) 필요할 때만.

설치: npm install -D @vitest/coverage-v8. 그 다음 config 나 CLI 로 활성화: npx vitest run --coverage.

다섯 아니라 네 숫자

Coverage 리포트가 네 퍼센트 보여줘:

  • Statements: 실행 가능 statement 의 어느 비율이 돌았는지.
  • Branches: 조건 branch (if/else, ternary) 의 어느 비율이 돌았는지.
  • Functions: 함수의 어느 비율이 호출됐는지.
  • Lines: 라인의 어느 비율이 돌았는지 (statements 와 비슷하지만 라인 단위).

Branches 가 가장 정보적. 높은 statement % 와 낮은 branch % 는 테스트가 happy path 만 치고 에러/edge branch skip 한다는 뜻. Branch 숫자가 보통 버그가 숨어있는 곳.

Coverage 가 측정하지 않는 것

Coverage 는 단언 품질 측정 안 해. 함수 호출하고 아무것도 단언 안 하는 테스트가 서른 개 단언하는 테스트와 같이 카운트. it('does the thing', () => { doTheThing(); }) 는 그 함수에 대해 full coverage 이고 zero 검증. Coverage 는 안 테스트된 코드 path 찾기에 써, 테스트된 코드가 맞다 주장에 쓰지 마.

Coverage 는 또 테스트 suite 유용성 측정 안 해. 실제로 ship 하는 버그를 못 잡는 100% coverage suite 가 잡는 60% suite 보다 나빠. 숫자는 도구지 목표가 아냐.

Coverage 는 진단이지 타겟이 아냐. 'Q3 까지 90% coverage 달성' 은 망한 목표 — 팀이 단언 없는 테스트 짜거나 어려운 파일 제외해서 달성. 유용한 목표는 '모든 critical path 가 최소 한 테스트 가짐' — coverage 가 뭐가 빠졌는지 찾는 거 도와, 뭐가 충분한지가 아니라.

Threshold — 천장 말고 바닥으로

vitest.config 에 coverage.thresholds 설정해서 coverage 가 숫자 아래로 떨어지면 run 실패시킬 수 있어. 맞는 사용법: 현재 실제 coverage 와 매칭되는 바닥으로 설정, 진짜 개선 일어날 때만 ratchet up. 잘못된 사용법: 40% coverage suite 에 80% 전체 설정하고 팀이 그걸 game 하는 거 보기.

카운트하면 안 되는 코드 제외

일부 파일은 진짜로 coverage 에 기여하면 안 돼: 타입 전용 파일, 생성된 코드, dev 전용 유틸, 테스트 파일 자체. coverage.exclude 설정해서 빼 — 노이즈 사라지면 퍼센트가 더 의미 있어져.

Code

설치 + coverage 로 실행·bash
# Install the v8 provider
npm install -D @vitest/coverage-v8

# Run with coverage
npx vitest run --coverage

# Coverage UI (alongside the regular vitest UI)
npx vitest --coverage --ui
Coverage config — provider, reporter, exclusion, threshold·typescript
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'happy-dom',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'json-summary', 'lcov'],
      reportsDirectory: './coverage',
      include: ['src/**/*.{ts,tsx}'],
      exclude: [
        'src/**/*.test.{ts,tsx}',
        'src/**/*.spec.{ts,tsx}',
        'src/**/__mocks__/**',
        'src/**/*.d.ts',
        'src/types/**',                 // type-only
        'src/main.tsx',                 // bootstrap, no logic
      ],
      thresholds: {
        // Floor — fail CI if any number drops below.
        statements: 70,
        branches: 65,
        functions: 70,
        lines: 70,
        // Per-file floor for critical paths
        'src/services/payment.ts': {
          statements: 95,
          branches: 90,
        },
      },
    },
  },
});
Coverage 리포트 실제 모양·text
% Statements   : 78.34 ( 245/313 )
% Branches     : 71.42 (  85/119 )
% Functions    : 81.25 (  52/64  )
% Lines        : 79.16 ( 247/312 )

File                          | % Stmts | % Branch | % Funcs | % Lines |
------------------------------|---------|----------|---------|---------|
All files                     |   78.34 |    71.42 |   81.25 |   79.16 |
 src/lib                      |   95.12 |    88.88 |  100.00 |   95.12 |
  format.ts                   |  100.00 |   100.00 |  100.00 |  100.00 |
  parse.ts                    |   88.88 |    75.00 |  100.00 |   88.88 |
 src/services                 |   62.45 |    54.12 |   71.42 |   63.10 |  ← needs work
  payment.ts                  |   42.18 |    28.57 |   50.00 |   42.18 |  ← critical, low
  notifications.ts            |   90.00 |    85.71 |  100.00 |   90.00 |
Inline 무시 — coverage 진짜 필요 없는 드문 라인에·typescript
// Per-line / per-block exclusion when you genuinely cannot test something
// (e.g., browser-only globals in Node, hardware paths)

export function getPlatform() {
  /* v8 ignore next 3 */
  if (typeof window === 'undefined') {
    return 'node';
  }
  return 'browser';
}

export function bootCriticalSystem() {
  /* v8 ignore start */
  if (process.env.SKIP_BOOT === 'true') {
    return;   // dev escape hatch — not part of production logic
  }
  /* v8 ignore stop */

  initializeWidgets();
}

External links

Exercise

프로젝트에 coverage 활성화 (@vitest/coverage-v8 + config 블록). npx vitest run --coverage 돌리고 생성된 coverage/index.html 열어. 가장 LOW branch coverage 파일 골라 — 열고, 안 테스트된 branch 하나 찾고, 그것 위한 테스트 추가. 다시 돌려서 숫자 올라가는 거 봐. 그 다음 그 파일의 현재 coverage 에 per-file threshold 설정해서 regression 안 되게 해.
Hint
파일이 0% coverage 보이면, 실제로 테스트가 도달하는 뭔가가 import 하는지 확인. 죽은 코드가 0% 로 보여 — 그건 테스트 짜라는 힌트 아니라 파일 지우라는 힌트.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.