단위 테스트가 부품을 검사한다면, E2E 테스트는 완성된 자동차를 직접 시운전하는 것입니다.

개념 정의

E2E 테스트 는 실제 브라우저에서 사용자의 전체 워크플로를 시뮬레이션하여 앱이 올바르게 동작하는지 검증합니다. Playwright는 Chromium, Firefox, WebKit을 모두 지원하는 E2E 테스트 프레임워크입니다.

설정

BASH
npm install -D @playwright/test
npx playwright install
JAVASCRIPT
// playwright.config.js
import { defineConfig } from '@playwright/test';

export default defineConfig({
  webServer: {
    command: 'npm run build && npm run preview',
    port: 4173,
  },
  testDir: 'tests',
  testMatch: /(.+\.)?test\.js/,
});

기본 테스트

JAVASCRIPT
// tests/home.test.js
import { test, expect } from '@playwright/test';

test('홈페이지가 올바르게 로드되어야 한다', async ({ page }) => {
  await page.goto('/');

  // 제목 확인
  await expect(page.locator('h1')).toContainText('환영합니다');

  // 네비게이션 링크 확인
  await expect(page.getByRole('link', { name: '블로그' })).toBeVisible();
  await expect(page.getByRole('link', { name: '소개' })).toBeVisible();
});

test('페이지 네비게이션이 동작해야 한다', async ({ page }) => {
  await page.goto('/');

  await page.getByRole('link', { name: '소개' }).click();
  await expect(page).toHaveURL('/about');
  await expect(page.locator('h1')).toContainText('소개');
});

폼 테스트

JAVASCRIPT
test('로그인 플로우', async ({ page }) => {
  await page.goto('/login');

  // 폼 입력
  await page.getByLabel('이메일').fill('test@example.com');
  await page.getByLabel('비밀번호').fill('password123');

  // 제출
  await page.getByRole('button', { name: '로그인' }).click();

  // 리다이렉트 확인
  await expect(page).toHaveURL('/dashboard');
  await expect(page.getByText('test@example.com')).toBeVisible();
});

test('유효성 검사 에러 표시', async ({ page }) => {
  await page.goto('/login');

  // 빈 폼 제출
  await page.getByRole('button', { name: '로그인' }).click();

  // 에러 메시지 확인
  await expect(page.getByText('이메일을 입력해주세요')).toBeVisible();
});

CRUD 워크플로 테스트

JAVASCRIPT
test.describe('블로그 포스트 CRUD', () => {
  test.beforeEach(async ({ page }) => {
    // 로그인
    await page.goto('/login');
    await page.getByLabel('이메일').fill('admin@test.com');
    await page.getByLabel('비밀번호').fill('admin123');
    await page.getByRole('button', { name: '로그인' }).click();
    await expect(page).toHaveURL('/dashboard');
  });

  test('새 포스트를 작성할 수 있어야 한다', async ({ page }) => {
    await page.goto('/posts/new');

    await page.getByLabel('제목').fill('테스트 포스트');
    await page.getByLabel('내용').fill('테스트 내용입니다.');
    await page.getByRole('button', { name: '저장' }).click();

    await expect(page.locator('h1')).toContainText('테스트 포스트');
  });

  test('포스트를 삭제할 수 있어야 한다', async ({ page }) => {
    await page.goto('/posts');

    // 삭제 확인 다이얼로그 처리
    page.on('dialog', dialog => dialog.accept());
    await page.getByRole('button', { name: '삭제' }).first().click();

    await expect(page.getByText('삭제되었습니다')).toBeVisible();
  });
});

반응형 테스트

JAVASCRIPT
test('모바일에서 햄버거 메뉴가 동작해야 한다', async ({ page }) => {
  // 모바일 뷰포트 설정
  await page.setViewportSize({ width: 375, height: 667 });
  await page.goto('/');

  // 데스크톱 네비게이션은 숨겨져야 함
  await expect(page.locator('nav.desktop')).not.toBeVisible();

  // 햄버거 메뉴 클릭
  await page.getByRole('button', { name: '메뉴' }).click();

  // 모바일 네비게이션 표시 확인
  await expect(page.getByRole('link', { name: '블로그' })).toBeVisible();
});

API 모킹

JAVASCRIPT
test('API 에러 시 에러 메시지를 표시해야 한다', async ({ page }) => {
  // API 응답 모킹
  await page.route('/api/posts', route => {
    route.fulfill({
      status: 500,
      body: JSON.stringify({ message: '서버 오류' }),
    });
  });

  await page.goto('/posts');
  await expect(page.getByText('데이터를 불러올 수 없습니다')).toBeVisible();
});

면접 포인트

  • "E2E 테스트와 단위 테스트의 비율은?": 테스트 피라미드에 따라 단위 테스트 > 통합 테스트 > E2E 테스트 순으로 많아야 합니다. E2E는 핵심 사용자 플로우(로그인, 결제 등)에 집중합니다.
  • "Playwright의 장점은?": 멀티 브라우저 지원, 자동 대기(auto-waiting), 네트워크 인터셉션, 트레이싱 기능이 내장되어 있어 안정적인 테스트를 작성할 수 있습니다.

정리

E2E 테스트는 "사용자가 실제로 경험하는 플로우"를 검증합니다. 모든 것을 E2E로 테스트할 필요는 없지만, 핵심 비즈니스 플로우(인증, 결제, CRUD)는 반드시 E2E로 보호해야 합니다. Playwright + SvelteKit은 설정이 간단하고 테스트가 안정적입니다.

댓글 로딩 중...