← 가이드 목록으로 ← Back to guides

JavaScript 테스팅과 디버깅 완벽 가이드 JavaScript Testing & Debugging Complete Guide

안정적인 소프트웨어는 철저한 테스트에서 시작됩니다. Jest/Vitest로 단위 테스트, Playwright로 E2E 테스트, Chrome DevTools로 효과적인 디버깅을 마스터하세요.

Reliable software starts with thorough testing. Master unit tests with Jest/Vitest, E2E tests with Playwright, and effective debugging with Chrome DevTools.

Vitest 단위 테스트 Vitest Unit Testing

Vitest는 Vite 기반의 빠른 테스트 러너입니다. Jest와 호환되는 API로 쉽게 마이그레이션할 수 있습니다.

Vitest is a fast test runner built on Vite. Easy migration with Jest-compatible API.

// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    coverage: {
      reporter: ['text', 'html'],
    },
  },
});
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'jsdom',
    coverage: {
      reporter: ['text', 'html'],
    },
  },
});
// utils.test.ts - 기본 테스트
import { describe, it, expect } from 'vitest';
import { add, multiply } from './utils';

describe('Math Utils', () => {
  it('두 숫자를 더한다', () => {
    expect(add(1, 2)).toBe(3);
  });

  it('두 숫자를 곱한다', () => {
    expect(multiply(3, 4)).toBe(12);
  });

  it('0을 곱하면 0이다', () => {
    expect(multiply(5, 0)).toBe(0);
  });
});
// utils.test.ts - Basic tests
import { describe, it, expect } from 'vitest';
import { add, multiply } from './utils';

describe('Math Utils', () => {
  it('adds two numbers', () => {
    expect(add(1, 2)).toBe(3);
  });

  it('multiplies two numbers', () => {
    expect(multiply(3, 4)).toBe(12);
  });

  it('returns 0 when multiplying by 0', () => {
    expect(multiply(5, 0)).toBe(0);
  });
});

Mock과 Spy Mocks and Spies

외부 의존성을 격리하여 테스트합니다. API 호출, 데이터베이스 등을 모킹합니다.

Isolate and test external dependencies. Mock API calls, databases, etc.

import { vi, describe, it, expect, beforeEach } from 'vitest';
import { fetchUser } from './api';

// 모듈 전체 모킹
vi.mock('./api');

describe('User Service', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('사용자 정보를 가져온다', async () => {
    // Mock 반환값 설정
    vi.mocked(fetchUser).mockResolvedValue({
      id: 1,
      name: '홍길동'
    });

    const user = await fetchUser(1);

    expect(user.name).toBe('홍길동');
    expect(fetchUser).toHaveBeenCalledWith(1);
  });
});
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { fetchUser } from './api';

// Mock entire module
vi.mock('./api');

describe('User Service', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('fetches user info', async () => {
    // Set mock return value
    vi.mocked(fetchUser).mockResolvedValue({
      id: 1,
      name: 'John Doe'
    });

    const user = await fetchUser(1);

    expect(user.name).toBe('John Doe');
    expect(fetchUser).toHaveBeenCalledWith(1);
  });
});

Playwright E2E 테스트 Playwright E2E Testing

Playwright는 실제 브라우저에서 사용자 시나리오를 테스트합니다. 크로스 브라우저 테스트를 지원합니다.

Playwright tests user scenarios in real browsers. Supports cross-browser testing.

// e2e/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('로그인 페이지', () => {
  test('유효한 자격증명으로 로그인', async ({ page }) => {
    // 페이지 이동
    await page.goto('/login');

    // 폼 입력
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'password123');

    // 제출
    await page.click('[data-testid="submit"]');

    // 결과 검증
    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toHaveText('대시보드');
  });

  test('잘못된 비밀번호로 에러 표시', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'wrong');
    await page.click('[data-testid="submit"]');

    await expect(page.locator('.error')).toBeVisible();
  });
});
// e2e/login.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Login Page', () => {
  test('login with valid credentials', async ({ page }) => {
    // Navigate
    await page.goto('/login');

    // Fill form
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'password123');

    // Submit
    await page.click('[data-testid="submit"]');

    // Verify result
    await expect(page).toHaveURL('/dashboard');
    await expect(page.locator('h1')).toHaveText('Dashboard');
  });

  test('shows error with wrong password', async ({ page }) => {
    await page.goto('/login');
    await page.fill('[data-testid="email"]', '[email protected]');
    await page.fill('[data-testid="password"]', 'wrong');
    await page.click('[data-testid="submit"]');

    await expect(page.locator('.error')).toBeVisible();
  });
});

Chrome DevTools 디버깅 Chrome DevTools Debugging

Chrome DevTools는 강력한 디버깅 도구입니다. 중단점, 콘솔, 네트워크 탭을 활용합니다.

Chrome DevTools is a powerful debugging tool. Utilize breakpoints, console, and network tab.

// 디버깅 기법들

// 1. debugger 문으로 중단점 설정
function processData(data) {
  debugger; // 여기서 실행 중단
  return data.map(item => item * 2);
}

// 2. 조건부 로깅
console.log('%c중요!', 'color: red; font-size: 20px');
console.table(users); // 테이블 형태로 출력
console.group('API 호출');
console.log('요청:', request);
console.log('응답:', response);
console.groupEnd();

// 3. 성능 측정
console.time('fetch');
await fetchData();
console.timeEnd('fetch'); // fetch: 123ms
// Debugging techniques

// 1. Set breakpoint with debugger statement
function processData(data) {
  debugger; // Execution stops here
  return data.map(item => item * 2);
}

// 2. Conditional logging
console.log('%cImportant!', 'color: red; font-size: 20px');
console.table(users); // Output as table
console.group('API Call');
console.log('Request:', request);
console.log('Response:', response);
console.groupEnd();

// 3. Performance measurement
console.time('fetch');
await fetchData();
console.timeEnd('fetch'); // fetch: 123ms

✨ 테스팅 Best Practices ✨ Testing Best Practices

  • AAA 패턴: Arrange(준비), Act(실행), Assert(검증)
  • 테스트 격리: 각 테스트가 독립적으로 실행
  • 커버리지 목표: 중요 로직 80% 이상 커버
  • E2E 최소화: 핵심 시나리오만 E2E로 테스트
  • AAA Pattern: Arrange, Act, Assert
  • Test Isolation: Each test runs independently
  • Coverage Goal: 80%+ coverage for critical logic
  • Minimize E2E: E2E only for critical scenarios

⚠️ 흔한 실수들 ⚠️ Common Mistakes

  • 구현 세부사항 테스트 (내부 구조 의존)
  • 너무 많은 Mock으로 실제 동작과 괴리
  • 비동기 테스트에서 await 누락
  • 테스트 간 상태 공유로 인한 불안정
  • Testing implementation details (internal structure dependency)
  • Too many mocks diverging from real behavior
  • Missing await in async tests
  • Instability from shared state between tests