Vi
Vitest는 vi
헬퍼를 통해 유용한 유틸리티 함수를 제공합니다. 이 함수들은 전역적으로 접근하거나(globals 설정이 활성화된 경우) vitest
에서 직접 가져와 사용할 수 있습니다.
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
호출은 호이스팅되므로 코드 어디에 위치하든 상관없이 항상 모든 가져오기(import) 전에 실행됩니다. 만약 범위 외부의 변수를 참조해야 한다면, 해당 변수를 vi.hoisted
내에서 정의하고 vi.mock
내에서 참조할 수 있습니다.
WARNING
vi.mock
은 import
키워드를 사용하여 가져온 모듈에만 작동하며, require
에는 작동하지 않습니다.
vi.mock
을 호이스팅하기 위해 Vitest는 파일을 정적으로 분석합니다. 이는 vitest
패키지에서 직접 가져오지 않은 vi
(예: 일부 유틸리티 파일에서 가져온 경우)는 사용할 수 없음을 의미합니다. vitest
에서 가져온 vi
와 함께 vi.mock
을 사용하거나 globals
구성 옵션을 활성화하십시오.
Vitest는 테스트 파일이 실행될 때 캐시되기 때문에 설정 파일 내에서 가져온 모듈을 모킹하지 않습니다. 테스트 파일을 실행하기 전에 모든 모듈 캐시를 지우려면 vi.hoisted
내에서 vi.resetModules()
를 호출하면 됩니다.
factory
함수가 정의되면, 해당 모듈을 가져오는 모든 호출은 이 함수의 결과를 반환합니다. Vitest는 팩토리 함수를 한 번만 호출하고, vi.unmock
또는 vi.doUnmock
이 호출될 때까지 모든 후속 가져오기에 대해 해당 결과를 캐시합니다.
jest
와 달리 팩토리 함수는 비동기일 수 있습니다. vi.importActual
또는 팩토리가 첫 번째 인수로 전달된 헬퍼를 사용하여 내부에서 원본 모듈을 가져올 수 있습니다.
Vitest 2.1부터 팩토리 함수 대신 spy
속성이 있는 객체를 제공할 수도 있습니다. spy
가 true
이면 Vitest는 평소와 같이 모듈을 자동 모킹하지만 내보내기 구현을 재정의하지는 않습니다. 이는 내보낸 메서드가 다른 메서드에 의해 올바르게 호출되었는지 확인하려는 경우에 유용합니다.
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
메서드에서 문자열 대신 모듈 프로미스를 지원합니다. 파일이 이동되면 경로가 자동으로 업데이트되고 importOriginal
은 자동으로 유형을 상속합니다. 이 시그니처를 사용하면 팩토리 반환 유형이 원본 모듈과 호환되도록 강제하며(내보내기는 선택 사항으로 유지됩니다).
// @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,
// 일부 내보내기 대체
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
메서드로 정의된 변수를 참조할 수도 있습니다.
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
기본 내보내기(default export)가 있는 모듈을 모킹하는 경우 반환된 팩토리 함수 객체 내에 default
키를 제공해야 합니다. 이는 ES 모듈별 주의 사항이므로 jest
는 CommonJS 모듈을 사용하므로 jest
문서는 다를 수 있습니다. 예를 들어,
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__
폴더에서 모듈로 사용할 파일을 찾습니다.
// increment.test.js
import { vi } from 'vitest';
// axios는 `__mocks__/axios.js`의 기본 내보내기입니다.
import axios from 'axios';
// increment는 `src/__mocks__/increment.js`의 이름 있는 내보내기입니다.
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는 원본 모듈을 가져오고 모든 내보내기를 자동 모킹합니다. 적용된 규칙은 알고리즘을 참조하십시오.
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의 모든 정적 가져오기는 항상 호이스팅된다는 점을 잊지 마십시오. 따라서 정적 가져오기 전에 이것을 배치해도 가져오기 전에 호출되도록 강제하지 않습니다.
vi.doMock('./increment.js'); // 이것은 import 문 _이후_에 호출됩니다.
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
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는 첫 번째 수준 값만 모킹되었다고 믿게 만듭니다. 실제로 전체 객체가 모킹된 경우 두 번째 인수로 { deep: true }
를 전달하여 TypeScript에 전체 객체가 모킹되었음을 알릴 수 있습니다.
// example.ts
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
// example.test.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>
모듈을 가져오고 모킹해야 하는지 여부에 대한 모든 검사를 우회합니다. 모듈을 부분적으로 모킹하려는 경우에 유용할 수 있습니다.
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, 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
과 동일하지만 파일 맨 위로 호이스팅되지 않습니다. 모듈의 다음 가져오기는 모킹 대신 원본 모듈을 가져옵니다. 이전에 가져온 모듈은 언모킹하지 않습니다.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment는 이미 모킹되었습니다. vi.mock이 호이스팅되었기 때문입니다.
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
모든 모듈의 캐시를 지워 모듈 레지스트리를 재설정합니다. 이렇게 하면 다시 가져올 때 모듈을 다시 평가할 수 있습니다. 최상위 가져오기는 다시 평가할 수 없습니다. 로컬 상태가 테스트 간에 충돌하는 모듈을 격리하는 데 유용할 수 있습니다.
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
모든 동적 가져오기가 로드될 때까지 기다립니다. 동기 호출이 다른 방법으로는 기다릴 수 없는 모듈 가져오기를 시작하는 경우에 유용합니다.
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
함수에 대한 스파이 함수를 생성하지만 함수 없이 시작할 수 있습니다. 함수가 호출될 때마다 호출 인수, 반환 값 및 this
컨텍스트를 저장합니다. 또한 메서드를 사용하여 동작을 조작할 수 있습니다. 함수가 주어지지 않으면 모킹 함수는 호출될 때 undefined
를 반환합니다.
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.isMockFunction
- 형태:
(fn: Function) => boolean
주어진 매개변수가 모킹 함수인지 확인합니다. TypeScript를 사용하는 경우 유형도 좁힙니다.
vi.clearAllMocks
모든 스파이 함수에서 .mockClear()
를 호출합니다. 이렇게 하면 모킹 기록(호출 횟수, 인수 등)이 지워지지만 구현은 기본값으로 재설정되지 않습니다.
vi.resetAllMocks
모든 스파이 함수에서 .mockReset()
를 호출합니다. 이렇게 하면 모킹 기록이 지워지고 구현이 빈 함수로 재설정됩니다(undefined
를 반환합니다).
vi.restoreAllMocks
모든 스파이 함수에서 .mockRestore()
를 호출합니다. 이렇게 하면 모킹 기록이 지워지고 구현이 원본으로 재설정됩니다.
vi.spyOn
- 형태:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
vi.fn()
과 유사하게 객체의 메서드 또는 getter/setter에 대한 스파이 함수를 생성합니다. 모킹 함수를 반환합니다.
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
afterEach
내에서 vi.restoreAllMocks
를 호출하거나 (test.restoreMocks
를 활성화하여) 모든 메서드를 원본 구현으로 복원할 수 있습니다. 이렇게 하면 원본 객체 디스크립터가 복원되므로 메서드의 구현을 변경할 수 없습니다.
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
브라우저 모드에서는 내보낸 메서드를 스파이할 수 없습니다. 대신 vi.mock("./file-path.js", { spy: true })
를 호출하여 모든 내보낸 메서드를 스파이할 수 있습니다. 이렇게 하면 모든 내보내기가 모킹되지만 구현은 그대로 유지되므로 메서드가 올바르게 호출되었는지 확인할 수 있습니다.
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 환경에서 내보내기를 스파이할 수 있지만 향후 변경될 수 있습니다.
vi.stubEnv
- 형태:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
process.env
및 import.meta.env
의 환경 변수 값을 변경합니다. vi.unstubAllEnvs
를 호출하여 값을 복원할 수 있습니다.
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
를 사용하여 이전 값을 복원할 수 없습니다.
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- 형태:
() => Vitest
vi.stubEnv
로 변경된 모든 import.meta.env
및 process.env
값을 복원합니다. 처음 호출될 때 Vitest는 원본 값을 기억하고 unstubAllEnvs
가 다시 호출될 때까지 저장합니다.
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
를 호출하여 원본 값을 복원할 수 있습니다.
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
를 사용하여 원본 값을 복원할 수 없습니다.
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
가 다시 호출될 때까지 저장합니다.
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
이 메서드는 지정된 밀리초가 경과하거나 타이머 큐가 비워질 때까지(둘 중 먼저 발생하는 것) 시작된 모든 타이머를 호출합니다.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- 형태:
(ms: number) => Promise<Vitest>
이 메서드는 지정된 밀리초가 경과하거나 타이머 큐가 비워질 때까지(둘 중 먼저 발생하는 것) 시작된 모든 타이머를 호출합니다. 여기에는 비동기적으로 설정된 타이머가 포함됩니다.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- 형태:
() => Vitest
다음 사용 가능한 타이머를 호출합니다. 각 타이머 호출 사이에 검증을 만드는 데 유용합니다. 직접 타이머를 관리하기 위해 체인 호출할 수 있습니다.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- 형태:
() => Promise<Vitest>
다음 사용 가능한 타이머를 호출하고 비동기적으로 설정된 경우 해결될 때까지 기다립니다. 각 타이머 호출 사이에 검증을 만드는 데 유용합니다.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.advanceTimersToNextFrame 2.1.0+
- 형태:
() => Vitest
vi.advanceTimersByTime
과 유사하지만 requestAnimationFrame
으로 현재 예약된 콜백을 실행하는 데 필요한 밀리초만큼 타이머를 진행합니다.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- 형태:
() => number
대기 중인 타이머 수를 가져옵니다.
vi.clearAllTimers
실행하도록 예약된 모든 타이머를 제거합니다. 이 타이머는 앞으로 절대 실행되지 않습니다.
vi.getMockedSystemTime
- 형태:
() => Date | null
setSystemTime
을 사용하여 설정된 모킹된 현재 날짜를 반환합니다. 날짜가 모킹되지 않은 경우 메서드는 null
을 반환합니다.
vi.getRealSystemTime
- 형태:
() => number
vi.useFakeTimers
를 사용하는 경우 Date.now
호출은 모킹됩니다. 밀리초 단위로 실제 시간을 가져와야 하는 경우 이 함수를 호출할 수 있습니다.
vi.runAllTicks
- 형태:
() => Vitest
process.nextTick
에 의해 큐에 추가된 모든 마이크로태스크를 호출합니다. 이렇게 하면 자체적으로 예약된 모든 마이크로태스크도 실행됩니다.
vi.runAllTimers
- 형태:
() => Vitest
이 메서드는 타이머 큐가 비워질 때까지 시작된 모든 타이머를 호출합니다. 즉, runAllTimers
중에 호출된 모든 타이머가 실행됩니다. 무한 간격이 있는 경우 10,000번 시도 후 예외가 발생합니다(fakeTimers.loopLimit
로 구성 가능).
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) {
clearInterval(interval);
}
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- 형태:
() => Promise<Vitest>
이 메서드는 타이머 큐가 비워질 때까지 시작된 모든 타이머를 비동기적으로 호출합니다. 즉, runAllTimersAsync
중에 호출된 모든 타이머가 비동기 타이머까지 실행됩니다. 무한 간격이 있는 경우 10,000번 시도 후 예외가 발생합니다(fakeTimers.loopLimit
로 구성 가능).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- 형태:
() => Vitest
이 메서드는 vi.useFakeTimers
호출 후에 시작된 모든 타이머를 호출합니다. 호출 중에 시작된 타이머는 실행하지 않습니다.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- 형태:
() => Promise<Vitest>
이 메서드는 vi.useFakeTimers
호출 후에 시작된 모든 타이머를 비동기적으로 호출합니다. 비동기 타이머까지 포함합니다. 호출 중에 시작된 타이머는 실행하지 않습니다.
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- 형태:
(date: string | number | Date) => void
가짜 타이머가 활성화된 경우 이 메서드는 사용자가 시스템 시계를 변경하는 것을 시뮬레이션합니다(hrtime
, performance.now
또는 new Date()
와 같은 날짜 관련 API에 영향을 미칩니다). 그러나 타이머는 실행하지 않습니다. 가짜 타이머가 활성화되지 않은 경우 이 메서드는 Date.*
호출만 모킹합니다.
현재 날짜에 의존하는 모든 것을 테스트해야 하는 경우에 유용합니다. 예를 들어 코드 내에서 Luxon 호출.
Date
와 동일한 문자열 및 숫자 인수를 허용합니다.
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
을 자동으로 모킹하지 않습니다. 하지만 toFake
인수에 옵션을 지정하여 활성화할 수 있습니다: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- 형태:
() => boolean
가짜 타이머가 활성화된 경우 true
를 반환합니다.
vi.useRealTimers
- 형태:
() => Vitest
타이머 모킹이 더 이상 필요하지 않으면 이 메서드를 호출하여 모킹된 타이머를 원본 구현으로 되돌릴 수 있습니다. 이전에 예약된 모든 타이머는 폐기됩니다.
기타
Vitest가 제공하는 유용한 헬퍼 함수 세트입니다.
vi.waitFor
- 형태:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
콜백이 성공적으로 실행될 때까지 기다립니다. 콜백이 오류를 발생시키거나 거부된 프로미스를 반환하면 성공하거나 시간 초과될 때까지 계속 기다립니다.
이는 서버를 시작하고 시작될 때까지 기다려야 하는 경우와 같이 일부 비동기 작업이 완료될 때까지 기다려야 할 때 매우 유용합니다.
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);
});
비동기 콜백에도 작동합니다.
// @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
을 사용하여 페이지에 요소가 나타날 때까지 기다린 다음 요소로 무언가를 할 수 있습니다.
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
문은 파일 맨 위로 호이스팅되므로 가져오기가 평가되기 전에 정의된 모든 코드는 실제로 가져오기가 평가된 후에 실행됩니다.
그러나 모듈을 가져오기 전에 날짜 모킹과 같은 일부 부작용을 호출하는 것이 유용할 수 있습니다.
이 제한을 우회하려면 정적 가져오기를 다음과 같이 동적 가져오기로 다시 작성할 수 있습니다.
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
vitest
를 실행할 때 vi.hoisted
메서드를 사용하여 자동으로 수행할 수 있습니다.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
이 메서드는 팩토리 함수에서 반환된 값을 반환합니다. 로컬에서 정의된 변수에 쉽게 액세스해야 하는 경우 vi.mock
팩토리 함수에서 해당 값을 사용할 수 있습니다.
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를 지원하지 않더라도 비동기적으로 호출될 수 있습니다.
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- 형태:
RuntimeConfig
현재 테스트 파일의 구성을 업데이트합니다. 이 메서드는 현재 테스트 파일에 영향을 미치는 구성 옵션만 지원합니다.
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
가 호출된 경우 이 메서드는 구성을 원본 상태로 재설정합니다.