Skip to content
Vitest 3
Main Navigation 가이드 & API구성브라우저 모드고급 API
3.2.0
2.1.9
1.6.1
0.34.6

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

한국어

English
简体中文
繁體中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
Italiano
Polski
Türkçe
čeština
magyar

외관

Sidebar Navigation

소개

Vitest를 선택하는 이유

시작하기

기능

Vitest 구성하기

API

테스트 API 참조

Mock 함수

Vi

expect

expectTypeOf

assert

assertType

가이드

명령줄 인터페이스

테스트 필터링

테스트 프로젝트

리포터

커버리지

스냅샷

모킹

병렬 처리

타입 검사

Vitest UI

소스 내 테스팅

테스트 컨텍스트

테스트 어노테이션

테스트 환경

매처 확장하기

IDE 통합

디버깅

일반적인 오류

마이그레이션 가이드

Vitest 3.0으로 마이그레이션

Jest에서 마이그레이션

성능

테스트 성능 프로파일링

성능 향상

브라우저 모드

고급 API

다른 테스트 러너와의 비교

이 페이지에서

Vi ​

Vitest는 개발을 돕기 위해 vi 헬퍼를 통해 유틸리티 함수를 제공합니다. 이 헬퍼는 전역적으로 접근할 수 있으며(globals 설정이 활성화된 경우), vitest에서 직접 임포트할 수도 있습니다:

js
import { vi } from 'vitest';

모듈 모킹 ​

이 섹션에서는 모듈 모킹 시 사용할 수 있는 API를 설명합니다. Vitest는 require()를 사용하여 임포트된 모듈을 모킹하는 것을 지원하지 않으니 유의하십시오.

vi.mock ​

  • 타입: (path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
  • 타입: <T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void

제공된 path에서 임포트된 모든 모듈을 다른 모듈로 대체합니다. 경로 내에서 설정된 Vite 별칭을 사용할 수 있습니다. vi.mock 호출은 호이스팅되므로, 어디에서 호출하든 관계없이 항상 모든 임포트보다 먼저 실행됩니다. 스코프 외부 변수를 참조해야 하는 경우, vi.hoisted 내에서 정의한 후 vi.mock 내에서 참조할 수 있습니다.

WARNING

vi.mock은 import 키워드로 임포트된 모듈에만 작동하며, require에는 작동하지 않습니다.

vi.mock을 호이스팅하기 위해 Vitest는 파일을 정적으로 분석합니다. 이는 vitest 패키지에서 직접 임포트되지 않은 vi(예: 일부 유틸리티 파일에서 가져온 vi)는 사용할 수 없음을 의미합니다. vitest에서 임포트된 vi와 함께 vi.mock을 사용하거나 globals 설정 옵션을 활성화하십시오.

Vitest는 설정 파일 내에서 임포트된 모듈을 모킹하지 않습니다. 테스트 파일이 실행될 때 이미 캐시되어 있기 때문입니다. 테스트 파일을 실행하기 전에 모든 모듈 캐시를 지우려면 vi.hoisted 내에서 vi.resetModules()를 호출할 수 있습니다.

factory 함수가 정의되면 모든 임포트는 그 결과를 반환합니다. Vitest는 팩토리 함수를 한 번만 호출하며, vi.unmock 또는 vi.doUnmock이 호출될 때까지 모든 후속 임포트에 대해 그 결과를 캐시합니다.

jest와는 다르게 팩토리 함수는 비동기적으로 동작할 수 있습니다. vi.importActual 또는 팩토리가 첫 번째 인수로 전달되는 헬퍼를 사용하여 원본 모듈에 접근할 수 있습니다.

팩토리 함수 대신 spy 속성을 포함하는 객체를 제공할 수도 있습니다. spy가 true이면 Vitest는 모듈을 평소와 같이 자동 모킹하지만, export의 구현을 재정의하지는 않습니다. 이는 export된 메서드가 다른 메서드에 의해 올바르게 호출되었는지 단순히 확인하려는 경우에 유용합니다.

ts
import { calculator } from './src/calculator.ts';

vi.mock('./src/calculator.ts', { spy: true });

// 원본 구현을 호출하지만,
// 나중에 동작을 확인할 수 있습니다.
const result = calculator(1, 2);

expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);

Vitest는 또한 더 나은 IDE 지원을 위해 vi.mock 및 vi.doMock 메서드에서 문자열 대신 모듈 Promise를 지원합니다. 파일이 이동되면 경로가 업데이트되고 importOriginal은 자동으로 타입을 추론합니다. 이 시그니처를 사용하면 팩토리 반환 타입이 원본 모듈과 호환되도록 강제됩니다(export는 선택 사항으로 유지).

ts
// @filename: ./path/to/module.js
export declare function total(...numbers: number[]): number;
// @filename: test.js
import { vi } from 'vitest';
// ---cut---
vi.mock(import('./path/to/module.js'), async importOriginal => {
  const mod = await importOriginal(); // 타입이 추론됩니다.
  //    ^?
  return {
    ...mod,
    // 일부 export를 대체합니다.
    total: vi.fn(),
  };
});

내부적으로 Vitest는 모듈 객체가 아닌 문자열을 기반으로 계속 작동합니다.

그러나 tsconfig.json에 paths 별칭이 구성된 TypeScript를 사용하는 경우 컴파일러가 임포트 타입을 올바르게 해결할 수 없습니다. 이를 위해 모든 별칭 임포트를 해당 상대 경로로 바꾸십시오. 예: import('@/module') 대신 import('./path/to/module.js')를 사용하십시오.

WARNING

vi.mock은 파일의 맨 위로 호이스팅됩니다(즉, 이동됩니다). 이는 beforeEach 또는 test 내부에 작성하더라도 실제로는 그보다 먼저 호출된다는 의미입니다.

이는 또한 팩토리 외부에서 정의된 변수를 팩토리 내부에서 사용할 수 없음을 의미합니다.

팩토리 내부에서 변수를 사용해야 한다면 vi.doMock을 사용해 보십시오. 이는 동일하게 작동하지만 호이스팅되지 않습니다. 다만, 후속 임포트만 모킹한다는 점에 유의하십시오.

vi.mock 이전에 선언된 경우 vi.hoisted 메서드로 정의된 변수를 참조할 수도 있습니다.

ts
import { namedExport } from './path/to/module.js';

const mocks = vi.hoisted(() => {
  return {
    namedExport: vi.fn(),
  };
});

vi.mock('./path/to/module.js', () => {
  return {
    namedExport: mocks.namedExport,
  };
});

vi.mocked(namedExport).mockReturnValue(100);

expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);

WARNING

기본 export가 있는 모듈을 모킹하는 경우, 반환되는 팩토리 함수 객체 내에 default 키를 제공해야 합니다. 이는 ES 모듈의 특정 주의 사항입니다. 따라서 jest는 CommonJS 모듈을 사용하므로 jest 문서는 다를 수 있습니다. 예를 들어,

ts
vi.mock('./path/to/module.js', () => {
  return {
    default: { myDefaultKey: vi.fn() },
    namedExport: vi.fn(),
    // 기타...
  };
});

모킹하려는 파일과 함께 __mocks__ 폴더가 있고 팩토리가 제공되지 않은 경우, Vitest는 __mocks__ 하위 폴더에서 동일한 이름의 파일을 찾아 실제 모듈로 사용하려고 시도합니다. 종속성을 모킹하는 경우, Vitest는 프로젝트의 루트(기본값: process.cwd())에서 __mocks__ 폴더를 찾으려고 시도합니다. deps.moduleDirectories 설정 옵션을 통해 Vitest에 종속성 위치를 지정할 수 있습니다.

예를 들어, 다음과 같은 파일 구조가 있습니다.

- __mocks__
  - axios.js
- src
  __mocks__
    - increment.js
  - increment.js
- tests
  - increment.test.js

테스트 파일에서 팩토리나 옵션 없이 vi.mock을 호출하면 __mocks__ 폴더에서 모듈로 사용할 파일을 찾습니다.

ts
import { vi } from 'vitest';

// axios는 `__mocks__/axios.js`의 기본 export입니다.
import axios from 'axios';

// increment는 `src/__mocks__/increment.js`의 이름 있는 export입니다.
import { increment } from '../increment.js';

vi.mock('axios');
vi.mock('../increment.js');

axios.get(`/apples/${increment(1)}`);

WARNING

vi.mock을 호출하지 않으면 모듈이 자동으로 모킹되지 않습니다. Jest의 자동 모킹 동작을 재현하려면 setupFiles 내에서 필요한 각 모듈에 대해 vi.mock을 호출할 수 있습니다.

__mocks__ 폴더가 없거나 팩토리 함수가 제공되지 않은 경우, Vitest는 원본 모듈을 임포트하고 모든 export를 자동 모킹합니다. 적용되는 규칙은 알고리즘을 참조하십시오.

vi.doMock ​

  • 타입: (path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
  • 타입: <T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void

vi.mock과 동일하지만 파일 맨 위로 호이스팅되지 않아 전역 파일 스코프에서 변수를 참조할 수 있습니다. 해당 모듈의 다음 동적 임포트는 모킹됩니다.

WARNING

이것은 호출되기 전에 임포트된 모듈을 모킹하지 않습니다. ESM의 모든 정적 임포트는 항상 호이스팅된다는 점을 명심하십시오. 따라서 정적 임포트 앞에 이를 두더라도 임포트 전에 호출되도록 강제하지 않습니다.

ts
vi.doMock('./increment.js'); // 이것은 import 문 _이후_에 호출됩니다.

import { increment } from './increment.js';
ts
export function increment(number) {
  return number + 1;
}
ts
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';

// vi.doMock이 아직 호출되지 않았으므로 모듈이 모킹되지 않습니다.
increment(1) === 2;

let mockedIncrement = 100;

beforeEach(() => {
  // 팩토리 내부에서 변수에 접근할 수 있습니다.
  vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});

test('다음 모듈을 임포트하면 모킹된 모듈이 임포트됩니다.', async () => {
  // vi.doMock이 임포트 _이후_에 평가되므로 원본 임포트는 모킹되지 않았습니다.
  expect(increment(1)).toBe(2);
  const { increment: mockedIncrement } = await import('./increment.js');
  // 새로운 동적 임포트는 모킹된 모듈을 반환합니다.
  expect(mockedIncrement(1)).toBe(101);
  expect(mockedIncrement(1)).toBe(102);
  expect(mockedIncrement(1)).toBe(103);
});

vi.mocked ​

  • 타입: <T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
  • 타입: <T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>

TypeScript를 위한 타입 헬퍼입니다. 전달된 객체를 반환할 뿐입니다.

partial이 true이면 반환 값으로 Partial<T>를 예상합니다. 기본적으로 TypeScript는 첫 번째 레벨 값만 모킹되었다고 인식합니다. 실제로 객체 전체가 모킹되었다고 TypeScript에 알리려면 두 번째 인수로 { deep: true }를 전달할 수 있습니다.

ts
export function add(x: number, y: number): number {
  return x + y;
}

export function fetchSomething(): Promise<Response> {
  return fetch('https://vitest.dev/');
}
ts
import * as example from './example';

vi.mock('./example');

test('1 + 1은 10과 같습니다.', async () => {
  vi.mocked(example.add).mockReturnValue(10);
  expect(example.add(1, 1)).toBe(10);
});

test('부분적으로만 올바른 타입으로 반환 값을 모킹합니다.', async () => {
  vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'));
  vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({
    ok: false,
  });
  // vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // 이것은 타입 에러입니다.
});

vi.importActual ​

  • 타입: <T>(path: string) => Promise<T>

모듈이 모킹되어야 하는지 여부에 대한 모든 검사를 우회하여 모듈을 임포트합니다. 모듈을 부분적으로 모킹하려는 경우 유용하게 사용할 수 있습니다.

ts
vi.mock('./example.js', async () => {
  const originalModule = await vi.importActual('./example.js');

  return { ...originalModule, get: vi.fn() };
});

vi.importMock ​

  • 타입: <T>(path: string) => Promise<MaybeMockedDeep<T>>

모든 속성(중첩 속성 포함)이 모킹된 모듈을 임포트합니다. vi.mock과 동일한 규칙을 따릅니다. 적용되는 규칙은 알고리즘을 참조하십시오.

vi.unmock ​

  • 타입: (path: string | Promise<Module>) => void

모킹된 레지스트리에서 모듈을 제거합니다. 모든 임포트 호출은 이전에 모킹되었더라도 원본 모듈을 반환합니다. 이 호출은 파일 맨 위로 호이스팅되므로, 예를 들어 setupFiles에 정의된 모듈만 언모킹합니다.

vi.doUnmock ​

  • 타입: (path: string | Promise<Module>) => void

vi.unmock과 동일하지만 파일 맨 위로 호이스팅되지 않습니다. 해당 모듈의 다음 임포트는 모의 대신 원본 모듈을 임포트합니다. 이는 이전에 임포트된 모듈을 언모킹하지 않습니다.

ts
export function increment(number) {
  return number + 1;
}
ts
import { increment } from './increment.js';

// vi.mock이 호이스팅되었으므로 increment는 이미 모킹되었습니다.
increment(1) === 100;

// 이것은 호이스팅되었고, 팩토리는 1행의 임포트 전에 호출됩니다.
vi.mock('./increment.js', () => ({ increment: () => 100 }));

// 모든 호출이 모킹되었고, `increment`는 항상 100을 반환합니다.
increment(1) === 100;
increment(30) === 100;

// 이것은 호이스팅되지 않으므로 다른 임포트는 언모킹된 모듈을 반환합니다.
vi.doUnmock('./increment.js');

// 이것은 여전히 100을 반환합니다. `vi.doUnmock`은 모듈을 재평가하지 않기 때문입니다.
increment(1) === 100;
increment(30) === 100;

// 다음 임포트는 언모킹되었고, 이제 `increment`는 count + 1을 반환하는 원본 함수입니다.
const { increment: unmockedIncrement } = await import('./increment.js');

unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;

vi.resetModules ​

  • 타입: () => Vitest

모든 모듈의 캐시를 지워 모듈 레지스트리를 재설정합니다. 이를 통해 재임포트될 때 모듈을 다시 평가할 수 있습니다. 최상위 임포트는 다시 평가될 수 없습니다. 로컬 상태가 테스트 간에 충돌하는 모듈을 격리하는 데 유용할 수 있습니다.

ts
import { vi } from 'vitest';

import { data } from './data.js'; // 각 테스트 전에 재평가되지 않습니다.

beforeEach(() => {
  vi.resetModules();
});

test('상태 변경', async () => {
  const mod = await import('./some/path.js'); // 재평가됩니다.
  mod.changeLocalState('new value');
  expect(mod.getLocalState()).toBe('new value');
});

test('모듈이 이전 상태를 가집니다.', async () => {
  const mod = await import('./some/path.js'); // 재평가됩니다.
  expect(mod.getLocalState()).toBe('old value');
});

WARNING

모의 레지스트리를 재설정하지 않습니다. 모의 레지스트리를 지우려면 vi.unmock 또는 vi.doUnmock을 사용하십시오.

vi.dynamicImportSettled ​

모든 임포트가 로드될 때까지 기다립니다. 동기 호출이 모듈 임포트를 시작하여 다른 방법으로는 기다릴 수 없는 경우에 유용합니다.

ts
import { expect, test } from 'vitest';

// Promise가 반환되지 않으므로 임포트를 추적할 수 없습니다.
function renderComponent() {
  import('./component.js').then(({ render }) => {
    render();
  });
}

test('작업이 해결됩니다.', async () => {
  renderComponent();
  await vi.dynamicImportSettled();
  expect(document.querySelector('.component')).not.toBeNull();
});

TIP

동적 임포트 중에 다른 동적 임포트가 시작되면 이 메서드는 모든 임포트가 완료될 때까지 기다립니다.

이 메서드는 임포트가 해결된 후 다음 setTimeout 틱까지 기다리므로, 모든 동기 작업은 해결 시점까지 완료되어야 합니다.

함수 및 객체 모킹 ​

이 섹션에서는 메서드 모킹과 환경 및 전역 변수 대체에 대해 설명합니다.

vi.fn ​

  • 타입: (fn?: Function) => Mock

함수에 대한 스파이를 생성하지만, 함수 없이도 초기화할 수 있습니다. 함수가 호출될 때마다 호출 인수, 반환 값, 그리고 인스턴스를 저장합니다. 또한 메서드를 사용하여 동작을 조작할 수 있습니다. 함수가 주어지지 않으면, 호출 시 모의는 undefined를 반환합니다.

ts
const getApples = vi.fn(() => 0);

getApples();

expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);

getApples.mockReturnValueOnce(5);

const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);

vi.mockObject 3.2.0+ ​

  • 타입: <T>(value: T) => MaybeMockedDeep<T>

vi.mock()이 모듈 export를 모킹하는 것과 동일한 방식으로, 주어진 객체의 속성과 메서드를 깊이 모킹합니다. 자세한 내용은 자동 모킹을 참조하십시오.

ts
const original = {
  simple: () => 'value',
  nested: {
    method: () => 'real',
  },
  prop: 'foo',
};

const mocked = vi.mockObject(original);
expect(mocked.simple()).toBe(undefined);
expect(mocked.nested.method()).toBe(undefined);
expect(mocked.prop).toBe('foo');

mocked.simple.mockReturnValue('mocked');
mocked.nested.method.mockReturnValue('mocked nested');

expect(mocked.simple()).toBe('mocked');
expect(mocked.nested.method()).toBe('mocked nested');

vi.isMockFunction ​

  • 타입: (fn: Function) => boolean

주어진 매개변수가 모의 함수인지 검사합니다. TypeScript를 사용하는 경우 타입도 추론하여 좁혀줍니다.

vi.clearAllMocks ​

모든 스파이에 대해 .mockClear()를 호출합니다. 이는 모의 구현에 영향을 주지 않으면서 모의 기록을 지웁니다.

vi.resetAllMocks ​

모든 스파이에 대해 .mockReset()을 호출합니다. 이는 모의 기록을 지우고 각 모의의 구현을 원본으로 재설정합니다.

vi.restoreAllMocks ​

모든 스파이에 대해 .mockRestore()를 호출합니다. 이는 모의 기록을 지우고, 모든 원본 모의 구현을 복원하며, 스파이된 객체의 원본 디스크립터도 복원합니다.

vi.spyOn ​

  • 타입: <T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance

vi.fn()과 유사하게 객체의 메서드 또는 getter/setter에 대한 스파이를 생성합니다. 이는 모의 함수를 반환합니다.

ts
let apples = 0;
const cart = {
  getApples: () => 42,
};

const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;

expect(cart.getApples()).toBe(1);

expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);

TIP

명시적 리소스 관리를 지원하는 환경에서는 const 대신 using을 사용하여 포함 블록이 종료될 때 모의 함수에 대해 mockRestore를 자동으로 호출할 수 있습니다. 이는 스파이된 메서드에 특히 유용합니다.

ts
it('console.log를 호출합니다.', () => {
  using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
  debug('message')
  expect(spy).toHaveBeenCalled()
})
// console.log가 여기서 복원됩니다.

TIP

afterEach 내에서 vi.restoreAllMocks를 호출하거나 (test.restoreMocks를 활성화하여) 모든 메서드를 원래 구현으로 복원할 수 있습니다. 이렇게 하면 원래 객체 디스크립터가 복원되므로, 메서드의 구현을 변경할 수 없습니다.

ts
const cart = {
  getApples: () => 42,
};

const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);

console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // 여전히 42!

TIP

브라우저 모드에서는 export된 메서드를 스파이하는 것이 불가능합니다. 대신 vi.mock("./file-path.js", { spy: true })를 호출하여 모든 export된 메서드를 스파이할 수 있습니다. 이렇게 하면 모든 export를 모킹하지만 구현은 그대로 유지됩니다. 따라서 메서드가 올바르게 호출되었는지 확인할 수 있습니다.

ts
import { calculator } from './src/calculator.ts';

vi.mock('./src/calculator.ts', { spy: true });

calculator(1, 2);

expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);

jsdom 또는 다른 Node.js 환경에서 export를 스파이하는 것이 가능하지만, 이는 미래에 변경될 수 있습니다.

vi.stubEnv ​

  • 타입: <T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest

process.env 및 import.meta.env의 환경 변수 값을 변경합니다. vi.unstubAllEnvs를 호출하여 값을 복원할 수 있습니다.

ts
import { vi } from 'vitest';

// `process.env.NODE_ENV` 및 `import.meta.env.NODE_ENV`는
// `vi.stubEnv` 호출 전에는 "development"입니다.

vi.stubEnv('NODE_ENV', 'production');

process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';

vi.stubEnv('NODE_ENV', undefined);

process.env.NODE_ENV === undefined;
import.meta.env.NODE_ENV === undefined;

// 다른 환경 변수는 변경하지 않습니다.
import.meta.env.MODE === 'development';

TIP

값을 단순히 할당하여 변경할 수도 있지만, vi.unstubAllEnvs를 사용하여 이전 값을 복원할 수는 없습니다.

ts
import.meta.env.MODE = 'test';

vi.unstubAllEnvs ​

  • 타입: () => Vitest

vi.stubEnv로 변경된 모든 import.meta.env 및 process.env 값을 복원합니다. 처음 호출될 때 Vitest는 원본 값을 기억하고 unstubAllEnvs가 다시 호출될 때까지 저장합니다.

ts
import { vi } from 'vitest';

// `process.env.NODE_ENV` 및 `import.meta.env.NODE_ENV`는
// `stubEnv` 호출 전에는 "development"입니다.

vi.stubEnv('NODE_ENV', 'production');

process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';

vi.stubEnv('NODE_ENV', 'staging');

process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';

vi.unstubAllEnvs();

// 첫 번째 "stubEnv" 호출 전에 저장된 값으로 복원됩니다.
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';

vi.stubGlobal ​

  • 타입: (name: string | number | symbol, value: unknown) => Vitest

전역 변수의 값을 변경합니다. vi.unstubAllGlobals를 호출하여 원래 값을 복원할 수 있습니다.

ts
import { vi } from 'vitest';

// `innerWidth`는 `stubGlobal` 호출 전에는 "0"입니다.

vi.stubGlobal('innerWidth', 100);

innerWidth === 100;
globalThis.innerWidth === 100;
// jsdom 또는 happy-dom을 사용하는 경우
window.innerWidth === 100;

TIP

값을 단순히 globalThis 또는 window(jsdom 또는 happy-dom 환경을 사용하는 경우)에 할당하여 변경할 수도 있지만, vi.unstubAllGlobals를 사용하여 원래 값을 복원할 수는 없습니다.

ts
globalThis.innerWidth = 100;
// jsdom 또는 happy-dom을 사용하는 경우
window.innerWidth = 100;

vi.unstubAllGlobals ​

  • 타입: () => Vitest

vi.stubGlobal로 변경된 globalThis/global(및 window/top/self/parent, jsdom 또는 happy-dom 환경을 사용하는 경우)의 모든 전역 값을 복원합니다. 처음 호출될 때 Vitest는 원본 값을 기억하고 unstubAllGlobals가 다시 호출될 때까지 저장합니다.

ts
import { vi } from 'vitest';

const Mock = vi.fn();

// IntersectionObserver는 "stubGlobal" 호출 전에는 "undefined"입니다.

vi.stubGlobal('IntersectionObserver', Mock);

IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// jsdom 또는 happy-dom을 사용하는 경우
window.IntersectionObserver === Mock;

vi.unstubAllGlobals();

globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// 정의되지 않았으므로 ReferenceError가 발생합니다.
IntersectionObserver === undefined;

가짜 타이머 ​

이 섹션에서는 가짜 타이머 사용 방법에 대해 설명합니다.

vi.advanceTimersByTime ​

  • 타입: (ms: number) => Vitest

이 메서드는 지정된 밀리초가 경과하거나 큐가 비워질 때까지(둘 중 먼저 발생하는 시점까지) 시작된 모든 타이머를 호출합니다.

ts
let i = 0;
setInterval(() => console.log(++i), 50);

vi.advanceTimersByTime(150);

// 로그: 1
// 로그: 2
// 로그: 3

vi.advanceTimersByTimeAsync ​

  • 타입: (ms: number) => Promise<Vitest>

이 메서드는 지정된 밀리초가 경과하거나 큐가 비워질 때까지(둘 중 먼저 발생하는 시점까지) 시작된 모든 타이머를 호출합니다. 여기에는 비동기적으로 설정된 타이머도 포함됩니다.

ts
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);

await vi.advanceTimersByTimeAsync(150);

// 로그: 1
// 로그: 2
// 로그: 3

vi.advanceTimersToNextTimer ​

  • 타입: () => Vitest

다음 사용 가능한 타이머를 호출합니다. 각 타이머 호출 사이에 단언을 수행하는 데 유용합니다. 타이머를 직접 관리하기 위해 체인 방식으로 호출할 수 있습니다.

ts
let i = 0;
setInterval(() => console.log(++i), 50);

vi.advanceTimersToNextTimer() // 로그: 1
  .advanceTimersToNextTimer() // 로그: 2
  .advanceTimersToNextTimer(); // 로그: 3

vi.advanceTimersToNextTimerAsync ​

  • 타입: () => Promise<Vitest>

다음 사용 가능한 타이머를 호출하고, 비동기적으로 설정된 경우 해결될 때까지 기다립니다. 각 타이머 호출 사이에 단언을 수행하는 데 유용합니다.

ts
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);

await vi.advanceTimersToNextTimerAsync(); // 로그: 1
expect(console.log).toHaveBeenCalledWith(1);

await vi.advanceTimersToNextTimerAsync(); // 로그: 2
await vi.advanceTimersToNextTimerAsync(); // 로그: 3

vi.advanceTimersToNextFrame 2.1.0+ ​

  • 타입: () => Vitest

vi.advanceTimersByTime과 유사하지만, requestAnimationFrame으로 현재 예약된 콜백을 실행하는 데 필요한 밀리초만큼 타이머를 진행합니다.

ts
let frameRendered = false;

requestAnimationFrame(() => {
  frameRendered = true;
});

vi.advanceTimersToNextFrame();

expect(frameRendered).toBe(true);

vi.getTimerCount ​

  • 타입: () => number

대기 중인 타이머의 개수를 반환합니다.

vi.clearAllTimers ​

실행 예정인 모든 타이머를 제거합니다. 이 타이머는 앞으로 절대 실행되지 않습니다.

vi.getMockedSystemTime ​

  • 타입: () => Date | null

모의된 현재 날짜를 반환합니다. 날짜가 모의되지 않은 경우 메서드는 null을 반환합니다.

vi.getRealSystemTime ​

  • 타입: () => number

vi.useFakeTimers를 사용할 때 Date.now 호출은 모의됩니다. 실제 시간을 밀리초 단위로 가져와야 한다면 이 함수를 호출할 수 있습니다.

vi.runAllTicks ​

  • 타입: () => Vitest

process.nextTick에 의해 큐에 추가된 모든 마이크로태스크를 호출합니다. 이는 자체적으로 예약된 모든 마이크로태스크도 실행합니다.

vi.runAllTimers ​

  • 타입: () => Vitest

이 메서드는 타이머 큐가 비워질 때까지 시작된 모든 타이머를 호출합니다. 즉, runAllTimers 중에 호출된 모든 타이머가 실행됩니다. 무한 간격이 있는 경우 10,000번 시도 후 예외가 발생합니다(fakeTimers.loopLimit으로 구성 가능).

ts
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
  console.log(++i);
  if (i === 3) {
    clearInterval(interval);
  }
}, 50);

vi.runAllTimers();

// 로그: 1
// 로그: 2
// 로그: 3

vi.runAllTimersAsync ​

  • 타입: () => Promise<Vitest>

이 메서드는 타이머 큐가 비워질 때까지 시작된 모든 타이머를 비동기적으로 호출합니다. 즉, runAllTimersAsync 중에 호출된 모든 타이머가 비동기 타이머라도 실행됩니다. 무한 간격이 있는 경우, 10,000번 시도 후 예외가 발생합니다(fakeTimers.loopLimit으로 구성 가능).

ts
setTimeout(async () => {
  console.log(await Promise.resolve('result'));
}, 100);

await vi.runAllTimersAsync();

// 로그: result

vi.runOnlyPendingTimers ​

  • 타입: () => Vitest

이 메서드는 vi.useFakeTimers 호출 이후에 시작된 모든 타이머를 호출합니다. 이 메서드 호출 중에 시작된 타이머는 실행하지 않습니다.

ts
let i = 0;
setInterval(() => console.log(++i), 50);

vi.runOnlyPendingTimers();

// 로그: 1

vi.runOnlyPendingTimersAsync ​

  • 타입: () => Promise<Vitest>

이 메서드는 vi.useFakeTimers 호출 이후에 시작된 모든 타이머를 비동기적으로 호출하며, 비동기 타이머도 포함합니다. 이 메서드 호출 중에 시작된 타이머는 실행하지 않습니다.

ts
setTimeout(() => {
  console.log(1);
}, 100);
setTimeout(() => {
  Promise.resolve().then(() => {
    console.log(2);
    setInterval(() => {
      console.log(3);
    }, 40);
  });
}, 10);

await vi.runOnlyPendingTimersAsync();

// 로그: 2
// 로그: 3
// 로그: 3
// 로그: 1

vi.setSystemTime ​

  • 타입: (date: string | number | Date) => void

가짜 타이머가 활성화된 경우, 이 메서드는 사용자가 시스템 시계를 변경하는 것을 시뮬레이션합니다(hrtime, performance.now 또는 new Date()와 같은 날짜 관련 API에 영향을 미칩니다). 하지만 타이머는 실행하지 않습니다. 가짜 타이머가 활성화되지 않은 경우, 이 메서드는 Date.* 호출만 모킹합니다.

현재 날짜에 의존하는 모든 것을 테스트해야 할 때 유용합니다. 예를 들어 코드 내의 Luxon 호출과 같은 경우입니다.

Date와 동일한 문자열 및 숫자 인수를 허용합니다.

ts
const date = new Date(1998, 11, 19);

vi.useFakeTimers();
vi.setSystemTime(date);

expect(Date.now()).toBe(date.valueOf());

vi.useRealTimers();

vi.useFakeTimers ​

  • 타입: (config?: FakeTimerInstallOpts) => Vitest

타이머 모킹을 활성화하려면 이 메서드를 호출해야 합니다. vi.useRealTimers()가 호출될 때까지 setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate, Date와 같은 모든 타이머 호출을 래핑합니다.

--pool=forks를 사용하여 node:child_process 내에서 Vitest를 실행할 때 nextTick 모킹은 지원되지 않습니다. NodeJS는 node:child_process 내부에서 process.nextTick을 사용하며, 모킹될 때 중단됩니다. --pool=threads로 Vitest를 실행할 때 nextTick 모킹은 지원됩니다.

구현은 내부적으로 @sinonjs/fake-timers를 기반으로 합니다.

TIP

vi.useFakeTimers()는 process.nextTick 및 queueMicrotask를 자동으로 모킹하지 않습니다. 하지만 toFake 인수에 옵션을 지정하여 활성화할 수 있습니다: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] }).

vi.isFakeTimers ​

  • 타입: () => boolean

가짜 타이머가 활성화되어 있으면 true를 반환합니다.

vi.useRealTimers ​

  • 타입: () => Vitest

타이머가 모두 실행되면 이 메서드를 호출하여 모의 타이머를 원래 구현으로 되돌릴 수 있습니다. 이전에 예약된 모든 타이머는 폐기됩니다.

기타 ​

Vitest가 제공하는 유용한 헬퍼 함수 모음입니다.

vi.waitFor ​

  • 타입: <T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>

콜백이 성공적으로 실행될 때까지 기다립니다. 콜백이 오류를 발생시키거나 거부된 Promise를 반환하면, 성공하거나 타임아웃될 때까지 계속 기다립니다.

옵션이 숫자로 설정되면 { timeout: options }를 설정하는 것과 동일한 효과를 가집니다.

이는 서버를 시작하고 서버가 시작될 때까지 기다려야 하는 경우처럼, 비동기 작업이 완료될 때까지 기다려야 할 때 매우 유용합니다.

ts
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';

test('서버가 성공적으로 시작되었습니다.', async () => {
  const server = createServer();

  await vi.waitFor(
    () => {
      if (!server.isReady) {
        throw new Error('서버가 시작되지 않았습니다.');
      }

      console.log('서버 시작됨');
    },
    {
      timeout: 500, // 기본값은 1000
      interval: 20, // 기본값은 50
    }
  );
  expect(server.isReady).toBe(true);
});

비동기 콜백에도 작동합니다.

ts
// @vitest-environment jsdom

import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';

test('요소가 DOM에 존재합니다.', async () => {
  // DOM 채우기 시작
  populateDOMAsync();

  const element = await vi.waitFor(
    async () => {
      // 요소가 존재할 때까지 요소를 가져오려고 시도합니다.
      const element = (await getDOMElementAsync()) as HTMLElement | null;
      expect(element).toBeTruthy();
      expect(element.dataset.initialized).toBeTruthy();
      return element;
    },
    {
      timeout: 500, // 기본값은 1000
      interval: 20, // 기본값은 50
    }
  );
  expect(element).toBeInstanceOf(HTMLElement);
});

vi.useFakeTimers가 사용되면 vi.waitFor는 각 확인 콜백에서 자동으로 vi.advanceTimersByTime(interval)을 호출합니다.

vi.waitUntil ​

  • 타입: <T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>

이것은 vi.waitFor와 유사하지만, 콜백이 오류를 발생시키면 즉시 실행이 중단되고 오류 메시지가 수신됩니다. 콜백이 거짓 값을 반환하면, 참 값이 반환될 때까지 다음 확인이 계속됩니다. 이는 다음 단계를 수행하기 전에 무언가가 존재할 때까지 기다려야 할 때 유용합니다.

아래 예시를 참조하십시오. vi.waitUntil을 사용하여 요소가 페이지에 나타날 때까지 기다린 다음, 해당 요소로 작업을 수행할 수 있습니다.

ts
import { expect, test, vi } from 'vitest';

test('요소가 올바르게 렌더링됩니다.', async () => {
  const element = await vi.waitUntil(() => document.querySelector('.element'), {
    timeout: 500, // 기본값은 1000
    interval: 20, // 기본값은 50
  });

  // 요소로 무언가를 합니다.
  expect(element.querySelector('.element-child')).toBeTruthy();
});

vi.hoisted ​

  • 타입: <T>(factory: () => T) => T

ES 모듈의 모든 정적 import 문은 파일 맨 위로 호이스팅되므로, 임포트 전에 정의된 모든 코드는 실제로는 임포트가 평가된 후에 실행됩니다.

그러나 모듈을 임포트하기 전에 날짜 모킹과 같은 일부 부수 효과를 호출하는 것이 유용할 수 있습니다.

이러한 제한을 우회하려면 정적 임포트를 다음과 같이 동적 임포트로 다시 작성할 수 있습니다.

diff
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')

vitest를 실행할 때 vi.hoisted 메서드를 사용하여 이를 자동으로 수행할 수 있습니다. 내부적으로 Vitest는 정적 임포트를 라이브 바인딩을 유지하면서 동적 임포트로 변환합니다.

diff
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())

임포트 사용 불가

임포트 전에 코드를 실행한다는 것은, 임포트된 변수가 아직 정의되지 않았으므로 접근할 수 없다는 것을 의미합니다.

ts
import { value } from './some/module.js';

vi.hoisted(() => { value }); // 오류 발생

이 코드는 다음 오류를 발생시킵니다.

Cannot access '__vi_import_0__' before initialization

vi.hoisted 내부에서 다른 모듈의 변수에 접근해야 하는 경우 동적 임포트를 사용하십시오.

ts
await vi.hoisted(async () => {
  const { value } = await import('./some/module.js');
});

그러나 vi.hoisted 내부에서 무엇이든 임포트하는 것은 권장되지 않습니다. 임포트는 이미 호이스팅되었기 때문입니다. 테스트가 실행되기 전에 무언가를 실행해야 한다면, 임포트된 모듈 자체에서 실행하십시오.

이 메서드는 팩토리에서 반환된 값을 반환합니다. 로컬에서 정의된 변수에 쉽게 접근해야 하는 경우, vi.mock 팩토리에서 이 값을 사용할 수 있습니다.

ts
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';

const { mockedMethod } = vi.hoisted(() => {
  return { mockedMethod: vi.fn() };
});

vi.mock('./path/to/module.js', () => {
  return { originalMethod: mockedMethod };
});

mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);

이 메서드는 환경이 최상위 await를 지원하지 않더라도 비동기적으로 호출될 수 있습니다.

ts
const json = await vi.hoisted(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts');
  return response.json();
});

vi.setConfig ​

  • 타입: RuntimeConfig

현재 테스트 파일의 설정을 업데이트합니다. 이 메서드는 현재 테스트 파일에 영향을 미치는 설정 옵션만 지원합니다.

ts
vi.setConfig({
  allowOnly: true,
  testTimeout: 10_000,
  hookTimeout: 10_000,
  clearMocks: true,
  restoreMocks: true,
  fakeTimers: {
    now: new Date(2021, 11, 19),
    // 전체 객체를 지원합니다.
  },
  maxConcurrency: 10,
  sequence: {
    hooks: 'stack',
    // "sequence.hooks"만 지원합니다.
  },
});

vi.resetConfig ​

  • 타입: RuntimeConfig

이전에 vi.setConfig가 호출된 경우, 이 메서드는 설정을 원래 상태로 재설정합니다.

Pager
이전Mock 함수
다음expect

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2021-Present Vitest Team

https://vitest.dev/api/vi

MIT 라이선스 하에 배포되었습니다.

Copyright (c) 2021-Present Vitest Team