Vitest로 테스트 전환: 설정과 모킹 전략
설정·모킹·비동기 테스트에서 덜 걸리는 전환 절차

핵심 요약
- Vitest는 Vite 설정을 공유해 변환 설정 중복을 줄이지만 테스트 환경은 명시해야 한다.
- vi.mock은 호이스팅되므로 팩토리 안에서 외부 변수를 참조하면 실패한다.
- 비동기 테스트는 반환 프로미스를 await하고, 타이머는 가짜 타이머로 검증한다.
- 모킹·핸들러를 테스트마다 초기화하지 않으면 실행 순서에 따라 결과가 달라진다.
테스트 러너를 교체하는 일은 "설정만 바꾸면 끝"처럼 보이지만, 실제로 발목을 잡는 것은 모킹과 실행 환경이다. 특히 프레임워크 설정을 공유하는 번들러와 테스트 러너를 함께 쓰면 변환 파이프라인이 겹치면서 예상치 못한 곳에서 실패한다. Vitest는 Vite 설정을 공유해 이런 중복을 줄이는 대신, 모킹과 환경 설정에서 알아 둘 차이가 있다. 이 글은 전환을 결정하는 기준부터 실제 실행 순서, 자주 걸리는 지점까지 정리한다.
전환을 결정하는 기준
전환 자체가 목적이 되어서는 안 된다. 다음 조건이 겹치면 Vitest 도입 이득이 크다.
- 이미 Vite 기반 프로젝트라 설정·모듈 해석 규칙을 공유하고 싶다.
- TypeScript와 ESM을 별도 변환 없이 그대로 쓰고 싶다.
- 테스트 실행 속도와 watch 모드의 반응성이 중요하다.
- 브라우저 환경 테스트와 Node 환경 테스트를 한 러너에서 나눠 돌리고 싶다.
설정 파일과 실행 환경 맞추기
Vitest는 Vite 설정을 읽어 모듈 해석과 변환을 그대로 사용한다. 그래서 별도 변환 설정을 다시 만들 필요가 없지만, 테스트 환경은 명시해야 한다.
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "jsdom",
setupFiles: ["./test/setup.ts"],
globals: true
}
});
DOM 환경·전역 설정
컴포넌트 테스트는 jsdom이나 happy-dom 같은 DOM 환경이 필요하고, 순수 로직은 Node 환경이면 충분하다. 환경을 파일 단위로 나누려면 디렉터리별 설정이나 프로젝트 구성을 사용한다. globals를 켜면 describe·it·expect를 import 없이 쓸 수 있지만, 타입 설정과 함께 켜지 않으면 에디터에서 오류가 난다.
모킹 방식 비교와 선택
Vitest 공식 가이드에 따르면 Vitest의 모킹은 Jest와 비슷하지만 vi 객체를 쓰고, vi.mock 호출이 파일 최상단으로 호이스팅된다는 차이가 있다. 이 호이스팅 때문에 모킹 대상이 실제 import보다 먼저 준비되므로, 팩토리 안에서 외부 변수를 참조하면 에러가 난다.
import { vi, describe, it, expect } from "vitest";
import { fetchUser } from "./api";
vi.mock("./api", () => ({
fetchUser: vi.fn()
}));
it("사용자 정보를 반환한다", async () => {
(fetchUser as any).mockResolvedValue({ id: 1, name: "Huni" });
await expect(fetchUser(1)).resolves.toMatchObject({ id: 1 });
});
모듈 전체를 대체할지, 스파이로 일부만 관찰할지, 네트워크만 가로챌지에 따라 선택이 달라진다. 네트워크 계층은 요청 라이브러리 수준에서 가로채는 편이 테스트 결합도를 낮춘다.
| 방식 | 적합한 경우 | 주의점 |
|---|---|---|
| vi.mock 모듈 대체 | 무거운 의존성·외부 모듈 분리 | 호이스팅으로 팩토리 외부 변수 참조 불가 |
| vi.spyOn | 일부 동작만 관찰·대체 | 원본 객체가 존재해야 함 |
| 네트워크 가로채기 | API 응답 시나리오 | 핸들러 정리 누락 시 테스트 오염 |
비동기·타이머 테스트 다루기
비동기 테스트에서 흔한 실패는 "기다리지 않고 단언"하는 것이다. 반환된 프로미스를 반드시 await하고, 상태 갱신을 기다릴 때는 waitFor 계열 유틸을 쓴다. 타이머에 의존하는 코드는 가짜 타이머로 시간을 앞당겨 검증한다.
vi.useFakeTimers();
const spy = vi.fn();
setTimeout(spy, 1000);
vi.advanceTimersByTime(1000);
expect(spy).toHaveBeenCalledOnce();
vi.useRealTimers();
가짜 타이머를 켠 채로 프로미스 해결을 기다리면 교착이 생길 수 있으므로, 비동기와 타이머를 섞는 테스트는 구간을 나눠 검증한다.
커버리지와 리포터 설정
커버리지는 별도 프로바이더를 설치해 켠다. 임계값을 정해 CI에서 강제할 수 있지만, 커버리지 숫자 자체를 목표로 삼으면 의미 없는 테스트가 늘어난다. 변경된 파일 중심으로 확인하는 편이 실용적이다. 리포터는 로컬에서는 읽기 쉬운 형태, CI에서는 기계가 읽는 형태로 나눈다. 린트·테스트 파이프라인은 코드 품질 규칙과 연결되므로 TypeScript 코드 품질 관리 글의 기준과 함께 정리하면 좋다.
변환 파이프라인과 타입 검사 분리
Vitest는 Vite 변환을 재사용하므로 별도 트랜스파일 설정을 만들지 않아도 된다. 다만 타입 검사는 테스트 러너와 분리해 두는 편이 낫다. 테스트 실행 중 타입 오류를 함께 잡으려 하면 실행이 느려지고, 실패 원인이 타입인지 로직인지 구분하기 어려워진다. 타입 검사는 별도 명령으로 돌리고 테스트는 동작 검증에 집중시킨다. 모노레포라면 프로젝트 구성을 사용해 패키지별 환경과 타임아웃을 다르게 두고, 변경된 패키지의 테스트만 선택 실행하도록 구성한다.
전환 후 흔한 실패와 대응
- 전역 누락: globals를 켰는데 타입 설정이 없어 에디터가 경고를 낸다.
- 모킹 호이스팅: 팩토리에서 외부 변수를 참조해 런타임 에러가 난다.
- 환경 혼동: DOM이 필요한 테스트를 Node 환경에서 돌려
document is not defined가 난다. - 정리 누락: 모킹·핸들러를 초기화하지 않아 테스트 순서에 따라 결과가 달라진다.
전환은 한 번에 끝내지 말고 다음 4단계 순서로 범위를 넓히며 검증한다.
vitest.config.ts에 기본environment와setupFiles를 선언하고 외부 의존성이 없는 순수 함수 테스트부터 실행한다.jest.fn()·jest.spyOn()호출을vi.fn()·vi.spyOn()으로 치환하고 호이스팅되는vi.mock팩토리 내부의 외부 변수 참조를 분리한다.- 타이머와 비동기 상태 갱신이 포함된 테스트에서
vi.useFakeTimers()와await누락을 점검하고afterEach에서 목 초기화를 강제한다. - CI 워크플로에서
tsc --noEmit타입 검사와 Vitest 실행 단계를 분리해 연결한다. 실패하는 테스트부터 먼저 고정하는 흐름은 AI 코딩 어시스턴트와 TDD 실습을 참고한다.
초보자가 자주 실수하는 포인트
- globals만 켜고 타입 설정을 하지 않아 에디터에서 전역 식별자 오류가 난다.
- vi.mock 팩토리에서 외부 변수를 참조해 런타임 에러를 만든다.
- DOM이 필요한 테스트를 Node 환경에서 실행해 document 접근 오류가 난다.
체크리스트
- 전환 이득이 있는지(Vite 사용·ESM·watch 반응성) 확인했다.
- 테스트 환경(jsdom/Node)과 setupFiles, globals 타입을 설정했다.
- 모듈 대체·스파이·네트워크 가로채기 중 목적에 맞는 모킹을 선택했다.
- 비동기 await와 가짜 타이머 사용 구간을 점검했다.
- 커버리지 임계값과 CI 리포터를 설정하고 모킹 정리를 확인했다.
자주 묻는 질문
Jest 테스트를 그대로 Vitest에서 돌릴 수 있나요?
상당 부분 호환되지만 전역 객체가 jest가 아니라 vi이고 모킹 호이스팅 규칙이 다르다. import를 바꾸고 모킹 팩토리를 점검해야 한다.
jsdom과 happy-dom 중 무엇을 쓰나요?
둘 다 DOM 환경을 제공한다. 프로젝트가 요구하는 API 범위와 속도에 따라 선택하되, 한 프로젝트에서는 하나로 통일하는 편이 예측 가능하다.
커버리지 100%를 목표로 해야 하나요?
아니다. 숫자를 목표로 하면 의미 없는 테스트가 늘어난다. 핵심 경로와 변경 파일 중심으로 확인하고 임계값은 현실적으로 설정한다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.