Advanced

테스트 전략 완전 가이드: Claude Code 활용

테스트 피라미드를 기반으로 Claude Code로 단위 테스트, 통합 테스트, E2E 테스트를 설계하고 구현하는 실전 가이드입니다.

Claude Code로 테스트 전략 설계하고 구현하기

테스트는 쓰는 편이 좋다는 걸 알아도 어느 수준까지, 무엇을 테스트할지 판단하기는 어렵습니다. Claude Code를 활용하면 테스트 피라미드를 기반으로 전략적인 테스트 설계와 구현을 효율적으로 진행할 수 있습니다.

테스트 피라미드 실천

> 프로젝트 테스트 전략을 제안해줘.
> 테스트 피라미드를 기반으로
> 단위 테스트, 통합 테스트, E2E 테스트의
> 비율과 대상을 정리해줘.
테스트 종류비율대상도구
단위 테스트70%비즈니스 로직, 유틸Vitest
통합 테스트20%API, DB 연동, 컴포넌트Vitest + Testing Library
E2E 테스트10%주요 사용자 플로우Playwright

단위 테스트 자동 생성

> src/services/ 아래 모든 함수의 단위 테스트를 생성해줘.
> 정상 케이스, 예외 케이스, 경계값까지 커버.
> 커버리지 80% 이상을 목표로.
// src/services/pricing.ts
export function calculatePrice(
  basePrice: number,
  quantity: number,
  discountRate: number
): number {
  if (quantity < 0) throw new Error("수량은 0 이상이어야 합니다");
  if (discountRate < 0 || discountRate > 1) {
    throw new Error("할인율은 0~1 범위여야 합니다");
  }
  const subtotal = basePrice * quantity;
  return Math.round(subtotal * (1 - discountRate));
}

// __tests__/services/pricing.test.ts
import { describe, it, expect } from "vitest";
import { calculatePrice } from "../../services/pricing";

describe("calculatePrice", () => {
  // 정상 케이스
  it("기본 단가 × 수량을 반환한다", () => {
    expect(calculatePrice(100, 5, 0)).toBe(500);
  });

  it("할인을 적용한다", () => {
    expect(calculatePrice(1000, 1, 0.1)).toBe(900);
  });

  it("소수점을 반올림한다", () => {
    expect(calculatePrice(333, 3, 0.1)).toBe(899);
  });

  // 경계값
  it("수량이 0이면 0을 반환한다", () => {
    expect(calculatePrice(100, 0, 0)).toBe(0);
  });

  it("할인율 100%이면 0을 반환한다", () => {
    expect(calculatePrice(100, 5, 1)).toBe(0);
  });

  // 예외 케이스
  it("음수 수량은 에러를 던진다", () => {
    expect(() => calculatePrice(100, -1, 0)).toThrow("수량은 0 이상");
  });

  it("범위를 벗어난 할인율은 에러를 던진다", () => {
    expect(() => calculatePrice(100, 1, 1.5)).toThrow("할인율");
    expect(() => calculatePrice(100, 1, -0.1)).toThrow("할인율");
  });
});

통합 테스트 생성

API 엔드포인트에 대한 통합 테스트를 생성합니다.

> /api/orders 엔드포인트의 통합 테스트를 만들어줘.
> 테스트용 DB로 실제 CRUD 동작을 검증하도록.
> 테스트 간 데이터 간섭이 없도록 해줘.
import { describe, it, expect, beforeEach, afterAll } from "vitest";
import request from "supertest";
import { app } from "../../app";
import { prisma } from "../../lib/prisma";

describe("Orders API Integration", () => {
  beforeEach(async () => {
    await prisma.order.deleteMany();
    await prisma.user.upsert({
      where: { id: "test-user" },
      update: {},
      create: { id: "test-user", email: "test@example.com", name: "Test" },
    });
  });

  afterAll(async () => {
    await prisma.$disconnect();
  });

  it("POST /api/orders 로 주문을 생성할 수 있다", async () => {
    const res = await request(app)
      .post("/api/orders")
      .set("Authorization", "Bearer test-token")
      .send({
        items: [{ productId: "P1", quantity: 2, price: 1000 }],
      });

    expect(res.status).toBe(201);
    expect(res.body.data.id).toBeDefined();
    expect(res.body.data.totalAmount).toBe("2000");
  });

  it("GET /api/orders 로 주문 목록을 가져올 수 있다", async () => {
    // 테스트 데이터 준비
    await prisma.order.create({
      data: {
        userId: "test-user",
        totalAmount: 5000,
        status: "confirmed",
      },
    });

    const res = await request(app)
      .get("/api/orders")
      .set("Authorization", "Bearer test-token");

    expect(res.status).toBe(200);
    expect(res.body.data).toHaveLength(1);
  });
});

E2E 테스트 생성

Playwright로 주요 사용자 플로우를 테스트합니다.

> 회원가입부터 로그인까지의 E2E 테스트를
> Playwright로 작성해줘.
import { test, expect } from "@playwright/test";

test.describe("회원가입 & 로그인 플로우", () => {
  test("신규 가입 후 대시보드까지 이동", async ({ page }) => {
    // 회원가입 페이지 접속
    await page.goto("/register");
    await expect(page.getByRole("heading", { name: "회원가입" })).toBeVisible();

    // 폼 입력
    await page.getByLabel("이메일").fill("newuser@example.com");
    await page.getByLabel("비밀번호").fill("SecurePass123");
    await page.getByLabel("비밀번호 확인").fill("SecurePass123");

    // 가입 실행
    await page.getByRole("button", { name: "가입" }).click();

    // 대시보드로 리다이렉트
    await expect(page).toHaveURL("/dashboard");
    await expect(page.getByText("환영합니다")).toBeVisible();
  });

  test("유효하지 않은 이메일이면 에러가 표시된다", async ({ page }) => {
    await page.goto("/register");
    await page.getByLabel("이메일").fill("invalid-email");
    await page.getByRole("button", { name: "가입" }).click();

    await expect(page.getByText("유효한 이메일")).toBeVisible();
  });
});

테스트 커버리지 개선

> 테스트 커버리지 리포트를 생성하고
> 커버리지가 낮은 파일에 테스트를 추가해줘.
> 목표 80% 이상.
# Claude Code가 실행하는 명령
npx vitest --coverage --run

TDD 기반 테스트 우선 개발은 TDD와 Claude Code의 궁합, CI/CD의 자동 실행은 CI/CD 파이프라인 구축 가이드, 디버깅에서의 테스트 활용은 디버깅 테크닉 완전 가이드에서 다루고 있습니다.

정리

테스트 전략의 핵심은 “무엇을 어떤 레벨에서 테스트할지”를 잘 정하는 것입니다. Claude Code에 테스트 피라미드를 의식하도록 지시하면 효율적이고 유효한 테스트 체계를 구축할 수 있습니다. 우선 단위 테스트부터 시작해 점진적으로 커버리지를 넓혀 가 보세요.

테스트 프레임워크의 자세한 내용은 Vitest 공식 문서, Claude Code에 대해서는 Anthropic 공식 문서를 참고하세요.

#Claude Code #testing #테스트 전략 #Vitest #Playwright

Claude Code 워크플로우를 한 단계 업그레이드하세요

지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.

무료 제공

무료 PDF: 5분 완성 Claude Code 치트시트

주요 명령어, 단축키, 프롬프트 예시를 A4 한 장에 정리했습니다.

PDF 다운로드
M

이 글을 작성한 사람

Masa

Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.