> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Vitest 디버그

> 테스트 디버깅하기

Vitest로 작성된 테스트를 효과적으로 디버깅하는 방법을 다룹니다.

## Vitest 디버깅 방법

Sonamu는 Vitest를 테스트 프레임워크로 사용합니다. 다양한 디버깅 방법을 제공합니다.

## VSCode 디버거 사용

### 1. launch.json 설정

`.vscode/launch.json`:

```json theme={null}
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Current Test File",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["vitest", "run", "${file}"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "sourceMaps": true,
      "resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
    },
    {
      "type": "node",
      "request": "launch",
      "name": "Debug All Tests",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["vitest", "run"],
      "cwd": "${workspaceFolder}",
      "console": "integratedTerminal",
      "sourceMaps": true
    }
  ]
}
```

### 2. 디버깅 실행

1. **테스트 파일 열기**
   * 디버깅하려는 `.test.ts` 파일 열기

2. **브레이크포인트 설정**
   * 줄 번호 왼쪽 클릭하여 빨간 점 표시

3. **디버거 시작**
   * F5 키 또는 "Run and Debug" 패널에서 "Debug Current Test File" 선택

4. **디버깅**
   * 브레이크포인트에서 멈추면 변수 검사
   * F10 (Step Over), F11 (Step Into) 등 사용

## 콘솔 로깅

### console.log 사용

가장 간단한 디버깅 방법:

```typescript theme={null}
test("사용자 생성", async () => {
  const email = "test@example.com";

  console.log("테스트 시작:", email);

  const user = await UserModel.create({ email });
  console.log("생성된 사용자:", user);

  expect(user.email).toBe(email);
});
```

### 구조화된 로깅

```typescript theme={null}
test("복잡한 객체 로깅", async () => {
  const data = {
    user: { id: 1, email: "test@example.com" },
    items: [1, 2, 3],
  };

  // 예쁘게 출력
  console.log(JSON.stringify(data, null, 2));

  // 또는
  console.dir(data, { depth: null });
});
```

## 특정 테스트만 실행

### test.only

```typescript theme={null}
// ✅ 이 테스트만 실행
test.only("디버깅할 테스트", async () => {
  const result = await complexOperation();
  expect(result).toBeDefined();
});

// ❌ 다른 테스트는 건너뜀
test("건너뛸 테스트", async () => {
  // ...
});
```

### describe.only

```typescript theme={null}
// ✅ 이 블록의 테스트들만 실행
describe.only("User API", () => {
  test("사용자 생성", async () => {
    // ...
  });

  test("사용자 조회", async () => {
    // ...
  });
});

// ❌ 다른 describe 블록은 건너뜀
describe("Order API", () => {
  test("주문 생성", async () => {
    // ...
  });
});
```

### CLI로 특정 테스트 실행

```bash theme={null}
# 특정 파일만
pnpm vitest run src/models/user.model.test.ts

# 특정 패턴
pnpm vitest run user

# 특정 테스트 이름
pnpm vitest run -t "사용자 생성"
```

## Watch 모드

### 자동 재실행

```bash theme={null}
# Watch 모드로 실행
pnpm vitest watch

# 또는
pnpm test --watch
```

파일을 수정하면 관련 테스트가 자동으로 재실행됩니다.

### Watch 모드 단축키

테스트 실행 중:

* `a`: 모든 테스트 재실행
* `f`: 실패한 테스트만 재실행
* `t`: 특정 테스트 필터
* `q`: 종료

## UI 모드 (vitest --ui)

### 실행

```bash theme={null}
pnpm vitest --ui
```

브라우저에서 `http://localhost:51204/__vitest__/` 열림

**기능:**

* 테스트 계층 구조 시각화
* 실패한 테스트 필터링
* 개별 테스트 재실행
* 코드 커버리지 확인
* 콘솔 출력 확인

## 에러 디버깅

### 상세한 에러 메시지

```typescript theme={null}
test("에러 메시지 개선", async () => {
  const userId = 999;

  // ❌ 기본 에러
  expect(await UserModel.findById(userId)).toBeDefined();

  // ✅ 상세한 에러 메시지
  const user = await UserModel.findById(userId);
  expect(user, `사용자 ID ${userId}를 찾을 수 없음`).toBeDefined();
});
```

### try-catch로 에러 확인

```typescript theme={null}
test("에러 상세 분석", async () => {
  try {
    await riskyOperation();
    expect.fail("에러가 발생해야 함");
  } catch (error) {
    // 에러 상세 확인
    console.log("에러 타입:", error.constructor.name);
    console.log("에러 메시지:", error.message);
    console.log("스택 트레이스:", error.stack);

    expect(error).toBeInstanceOf(BadRequestException);
  }
});
```

## 비동기 코드 디버깅

### async/await 확인

```typescript theme={null}
test("비동기 작업 디버깅", async () => {
  console.log("1. 시작");

  const promise1 = fetchUser(1);
  console.log("2. fetchUser 호출");

  const promise2 = fetchOrders(1);
  console.log("3. fetchOrders 호출");

  const [user, orders] = await Promise.all([promise1, promise2]);
  console.log("4. 모두 완료:", { user, orders });

  expect(user).toBeDefined();
});
```

### 타임아웃 증가

```typescript theme={null}
test("느린 작업", async () => {
  // 기본 5초에서 30초로 증가
  const result = await slowOperation();
  expect(result).toBeDefined();
}, 30000); // 30초 타임아웃
```

## 모킹 디버깅

### Mock 호출 확인

```typescript theme={null}
import { vi } from "vitest";

test("Mock 호출 디버깅", async () => {
  const mockFn = vi.fn().mockResolvedValue({ id: 1 });

  await mockFn("test");
  await mockFn("test2");

  // Mock 호출 확인
  console.log("호출 횟수:", mockFn.mock.calls.length);
  console.log("첫 번째 호출:", mockFn.mock.calls[0]);
  console.log("모든 호출:", mockFn.mock.calls);

  expect(mockFn).toHaveBeenCalledTimes(2);
  expect(mockFn).toHaveBeenCalledWith("test");
});
```

### Mock 리셋 확인

```typescript theme={null}
beforeEach(() => {
  // 각 테스트 전에 Mock 리셋
  vi.clearAllMocks();
});

test("Mock 상태 확인", () => {
  const mockFn = vi.fn();

  // 호출 전
  console.log("호출 전:", mockFn.mock.calls.length); // 0

  mockFn("test");

  // 호출 후
  console.log("호출 후:", mockFn.mock.calls.length); // 1
});
```

## 데이터베이스 상태 확인

### 쿼리 로깅

```typescript theme={null}
test("DB 쿼리 확인", async () => {
  // 쿼리 로깅 활성화 (개발 중)
  const result = await UserModel.getPuri("r")
    .table("users")
    .select("*")
    .where({ email: "test@example.com" })
    .debug() // 쿼리 출력
    .getOne();

  console.log("결과:", result);
});
```

### 테스트 후 데이터 확인

```typescript theme={null}
test("데이터 생성 확인", async () => {
  const email = "test@example.com";

  await UserModel.create({ email });

  // DB에서 직접 확인
  const users = await UserModel.getPuri("r").table("users").select("*").getMany();

  console.log("전체 사용자:", users);

  const created = users.find((u) => u.email === email);
  console.log("생성된 사용자:", created);

  expect(created).toBeDefined();
});
```

## Coverage 디버깅

### 커버리지 확인

```bash theme={null}
# 커버리지 생성
pnpm test --coverage

# HTML 리포트 열기
open coverage/index.html
```

### 커버되지 않은 코드 찾기

```typescript theme={null}
// src/models/user.model.ts
class UserModelClass extends BaseModelClass {
  async createUser(email: string) {
    if (!email) {
      // ❌ 이 분기가 테스트되지 않음
      throw new Error("Email required");
    }

    return this.save({ email });
  }
}

// 테스트 추가 필요
test("이메일 없이 생성 시 에러", async () => {
  await expect(UserModel.createUser("")).rejects.toThrow("Email required");
});
```

## Vitest 설정 디버깅

### 설정 확인

`vitest.config.ts`:

```typescript theme={null}
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globals: true,
    include: ["src/**/*.test.ts"],

    // 디버깅 옵션
    bail: 1, // 첫 실패 시 중단
    reporters: ["verbose"], // 상세 출력
    logHeapUsage: true, // 메모리 사용량 출력

    // 타임아웃 증가
    testTimeout: 10000,
    hookTimeout: 10000,
  },
});
```

### 환경 변수 확인

```typescript theme={null}
test("환경 변수 디버깅", () => {
  console.log("NODE_ENV:", process.env.NODE_ENV);
  console.log("DB_HOST:", process.env.DB_HOST);
  console.log("모든 환경 변수:", process.env);
});
```

## 병렬 실행 디버깅

### 격리 모드 비활성화

일부 테스트는 병렬 실행 시 문제가 발생할 수 있습니다:

```typescript theme={null}
// vitest.config.ts
export default defineConfig({
  test: {
    pool: "forks",
    maxWorkers: 1, // 단일 워커
    isolate: false, // 격리 비활성화
  },
});
```

### 순차 실행

```bash theme={null}
# 순차 실행
pnpm vitest run --no-threads
```

## 스냅샷 디버깅

### 스냅샷 업데이트

```bash theme={null}
# 스냅샷 업데이트
pnpm vitest run -u

# 특정 파일만
pnpm vitest run -u src/models/user.model.test.ts
```

### 스냅샷 확인

```typescript theme={null}
test("스냅샷 디버깅", () => {
  const data = {
    id: 1,
    name: "Test",
    timestamp: new Date().toISOString(),
  };

  // ❌ timestamp가 계속 변경됨
  expect(data).toMatchSnapshot();

  // ✅ 동적 값 제외
  expect({
    ...data,
    timestamp: expect.any(String),
  }).toMatchSnapshot();
});
```

## 타입 체크 디버깅

### 타입 에러 확인

```bash theme={null}
# 타입 체크만 실행
pnpm vitest typecheck

# 또는
pnpm tsc --noEmit
```

### 타입 테스트

```typescript theme={null}
// src/**/*type-safety.test.ts
import { expectTypeOf } from "vitest";

test("타입 안전성 확인", () => {
  const user = { id: 1, email: "test@example.com" };

  expectTypeOf(user.id).toEqualTypeOf<number>();
  expectTypeOf(user.email).toEqualTypeOf<string>();
});
```

## 테스트 격리 문제

### beforeEach/afterEach 확인

```typescript theme={null}
describe("User Tests", () => {
  let testUser: User;

  beforeEach(async () => {
    console.log("Setup 시작");
    testUser = await UserModel.create({
      email: "test@example.com",
    });
    console.log("Setup 완료:", testUser.id);
  });

  afterEach(async () => {
    console.log("Cleanup 시작");
    if (testUser) {
      await UserModel.del(testUser.id);
    }
    console.log("Cleanup 완료");
  });

  test("테스트 1", async () => {
    expect(testUser).toBeDefined();
  });
});
```

## 메모리 누수 디버깅

### 메모리 사용량 확인

```typescript theme={null}
// vitest.config.ts
export default defineConfig({
  test: {
    logHeapUsage: true, // 메모리 사용량 출력
  },
});
```

### 리소스 정리 확인

```typescript theme={null}
test("리소스 정리 확인", async () => {
  const db = createConnection();

  try {
    await db.query("SELECT 1");
  } finally {
    await db.destroy(); // 반드시 정리
    console.log("DB 연결 정리 완료");
  }
});
```

## Best Practices

### 1. 작은 단위로 테스트

```typescript theme={null}
// ❌ 너무 큰 테스트
test("전체 사용자 플로우", async () => {
  // 생성, 조회, 수정, 삭제 모두 포함
});

// ✅ 작은 단위로 분리
test("사용자 생성", async () => {});
test("사용자 조회", async () => {});
test("사용자 수정", async () => {});
test("사용자 삭제", async () => {});
```

### 2. 의미 있는 테스트 이름

```typescript theme={null}
// ❌ 불명확한 이름
test("test1", async () => {});

// ✅ 명확한 이름
test("이메일 없이 사용자 생성 시 BadRequestException 발생", async () => {
  await expect(UserModel.create({ email: "" })).rejects.toThrow(BadRequestException);
});
```

### 3. AAA 패턴 (Arrange-Act-Assert)

```typescript theme={null}
test("사용자 생성", async () => {
  // Arrange (준비)
  const email = "test@example.com";
  const name = "Test User";

  // Act (실행)
  const user = await UserModel.create({ email, name });

  // Assert (검증)
  expect(user.email).toBe(email);
  expect(user.name).toBe(name);
});
```

### 4. 실패 시 충분한 정보

```typescript theme={null}
test("복잡한 조건 검증", async () => {
  const result = await complexCalculation();

  // ✅ 실패 시 무엇이 잘못되었는지 명확히
  expect(result.total, `total이 ${result.expected}여야 하는데 ${result.total}임`).toBe(
    result.expected,
  );
});
```

## 관련 문서

* [Naite 사용하기](/ko/troubleshooting/debugging/using-naite)
* [테스트 작성하기](/ko/testing/writing-tests/test-structure)
* [Naite로 디버깅하기](/ko/testing/naite/debugging-tests)
* [소스 맵](/ko/troubleshooting/debugging/source-maps)
