vitest hoisting — vi.mock factory 내 외부 변수 참조 시 ReferenceError¶
문제¶
vi.mock factory 내부에서 파일 상단에 선언한 const 변수를 참조하면 테스트 실행 시 ReferenceError: Cannot access 'X' before initialization가 발생한다.
재현 조건:
const capturedProps = {} as SomeType; // 파일 상단 const 선언
vi.mock("@/components/ui/dialog", () => ({
DialogContent: ({ onEscapeKeyDown }) => {
capturedProps.onEscapeKeyDown = onEscapeKeyDown; // ← ReferenceError!
return <div />;
},
}));
증상¶
- 테스트 실행 시
ReferenceError: Cannot access 'capturedProps' before initialization - 빌드·린트는 통과 (TypeScript는 hoisting을 정적으로 잡지 않음)
- vitest 실행 시점에만 에러 노출
시도했으나 실패한 것¶
const→let으로 변경 → 동일한 ReferenceError 발생 (TDZ는 let도 적용됨)- factory를 파일 최하단으로 이동 → vitest가 다시 상단으로 hoisting하므로 무의미
- 변수 선언을
var로 변경 → 값 참조 시점에 여전히 undefined
해결¶
vi.hoisted()로 factory 실행 이전에 변수를 확보한다.
// ✅ vi.hoisted로 factory 이전에 변수를 확보
const { capturedProps } = vi.hoisted(() => ({
capturedProps: {} as SomeType,
}));
vi.mock("@/components/ui/dialog", () => ({
DialogContent: ({ onEscapeKeyDown }) => {
capturedProps.onEscapeKeyDown = onEscapeKeyDown; // 안전하게 참조 가능
return <div />;
},
}));
여러 hoisted 변수가 필요하면 객체로 묶어서 한 번에 선언한다:
const { capturedProps, mockState } = vi.hoisted(() => ({
capturedProps: {} as CapturedProps,
mockState: { isPending: false },
}));
원인¶
vitest는 ESM static analysis 단계에서 vi.mock()을 파일 최상단으로 hoisting한다. 따라서 vi.mock factory는 파일의 다른 import나 변수 선언보다 먼저 실행된다. 일반 const/let은 TDZ(Temporal Dead Zone) 때문에 선언 전 접근 시 ReferenceError가 발생한다.
vi.hoisted()는 hoisting 시스템에 의해 vi.mock보다 더 일찍 실행되도록 보장된다. 따라서 factory가 실행될 시점에 이미 변수가 초기화되어 있다.
실행 순서: 1. vi.hoisted() 콜백 실행 → 변수 초기화 2. vi.mock() factory 실행 → 변수 참조 가능 3. import 문 실행 4. 나머지 파일 코드 실행
재발 방지¶
vi.mockfactory 내부에서 외부 변수를 참조해야 한다면 무조건vi.hoisted()로 감싼다- 테스트에서 상태를 mock factory와 공유해야 할 때 (예:
isPending플래그 toggle) 동일하게 적용 beforeEach에서 hoisted 변수를 리셋하여 테스트 간 상태 오염을 방지한다:- 프로젝트 개발 규칙(
개발_규칙_FE.md)의 테스트 섹션에 이 패턴을 명시
관련 자료¶
- 실제 적용 커밋: AssetManagement/Frontend
4e01d30(Phase 9-C.5) - 적용 파일:
src/components/common/AssignBatchModal/AssignBatchModal.handlers.test.tsx - 선행 기록: 2026-04-10-vitest-rtl-vite-tailwindv4-테스트-인프라-첫-도입-패턴 — vitest 인프라 셋업
- 관련 패턴: 2026-04-10-radix-dismiss-happydom-한계-dialog-mock-2층-테스트-구조 — 이 버그가 발견된 2층 구조
- vitest 공식 문서: https://vitest.dev/api/vi.html#vi-hoisted