테스트 러너
WARNING
이것은 고급 API입니다. 단순히 테스트를 실행하고 싶다면 이 API가 필요하지 않을 수 있습니다. 주로 라이브러리 작성자가 사용합니다.
구성 파일의 runner
옵션을 사용하여 테스트 러너의 경로를 설정할 수 있습니다. 이 파일은 다음 메서드를 구현하는 클래스를 기본 내보내기로 해야 합니다.
export interface VitestRunner {
/**
* 실제로 테스트를 수집하고 실행하기 전에 가장 먼저 호출되는 메서드입니다.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* 테스트 수집 후 "onBeforeRun" 이전에 호출됩니다.
*/
onCollected?: (files: File[]) => unknown;
/**
* 테스트 러너가 다음 테스트 실행을 취소해야 할 경우 호출됩니다.
* 러너는 이 메서드를 수신하여 호출 시 "onBeforeRunSuite" 및 "onBeforeRunTask"에서 테스트와 스위트를 건너뛴 것으로 표시해야 합니다.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* 단일 테스트를 실행하기 전에 호출됩니다. 이때 "result"는 아직 없습니다.
*/
onBeforeRunTest?: (test: TaskPopulated) => unknown;
/**
* 실제로 테스트 함수를 실행하기 전에 호출됩니다. 이때 "result"는 이미 "state"와 "startTime"을 포함하고 있습니다.
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 결과와 상태가 설정된 후에 호출됩니다.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* 테스트 함수를 실행한 직후에 호출됩니다. 이때 새로운 상태는 아직 없습니다. 테스트 함수에서 예외가 발생하면 호출되지 않습니다.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 단일 스위트를 실행하기 전에 호출됩니다. 이때 "result"는 아직 없습니다.
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* 단일 스위트를 실행한 후에 호출됩니다. 이때 상태와 결과가 존재합니다.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* 정의되어 있다면 일반적인 Vitest 스위트 분할 및 처리 대신 호출됩니다.
* "before" 및 "after" 훅은 무시되지 않습니다.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* 정의되어 있다면 일반적인 Vitest 처리 대신 호출됩니다. 사용자 정의 테스트 함수가 있는 경우 유용합니다.
* "before" 및 "after" 훅은 무시되지 않습니다.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* 작업이 업데이트될 때 호출됩니다. 리포터의 "onTaskUpdate"와 동일하지만 테스트와 동일한 스레드에서 실행됩니다.
*/
onTaskUpdate?: (task: [string, TaskResult | undefined][]) => Promise<void>;
/**
* 수집된 경로의 모든 테스트를 실행하기 전에 호출됩니다.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* 수집된 경로의 모든 테스트를 실행한 직후에 호출됩니다.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* 테스트에 대한 새로운 컨텍스트가 정의될 때 호출됩니다. 컨텍스트에 사용자 정의 속성을 추가하려는 경우 유용합니다.
* 러너를 사용하여 사용자 정의 컨텍스트만 정의하려면 대신 "setupFiles"에서 "beforeAll"을 사용하는 것을 고려해 보십시오.
*
* 이 메서드는 "test"와 "custom" 핸들러 모두에 대해 호출됩니다.
*
* @see https://www.getbook.com/en/book/vitest-2/advanced/runner#your-task-function
*/
extendTaskContext?: <T extends Test | Custom>(
context: TaskContext<T>
) => TaskContext<T>;
/**
* 특정 파일이 가져올 때 호출됩니다. 테스트를 수집할 때와 설정 파일을 가져올 때 두 가지 상황에서 호출될 수 있습니다.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* 공개적으로 사용 가능한 구성입니다.
*/
config: VitestRunnerConfig;
}
이 클래스를 초기화할 때 Vitest는 Vitest 구성을 전달하며, 이를 config
속성으로 노출해야 합니다.
WARNING
Vitest는 또한 ViteNodeRunner
인스턴스를 __vitest_executor
속성으로 주입합니다. importFile
메서드에서 파일을 처리하는 데 사용할 수 있습니다 (이는 TestRunner
및 BenchmarkRunner
의 기본 동작입니다).
ViteNodeRunner
는 executeId
메서드를 노출하며, 이는 Vite 친화적인 환경에서 테스트 파일을 가져오는 데 사용됩니다. 즉, Node가 이해할 수 있도록 런타임에 가져오기를 해결하고 파일 내용을 변환합니다.
TIP
스냅샷 지원 및 기타 일부 기능은 러너에 따라 다릅니다. 이 기능을 유지하고 싶다면 vitest/runners
에서 가져온 VitestTestRunner
에서 러너를 확장할 수 있습니다. 벤치마크 기능을 확장하고 싶다면 BenchmarkNodeRunner
도 사용할 수 있습니다.
작업
스위트와 테스트는 내부적으로 tasks
라고 불립니다. Vitest 러너는 테스트를 수집하기 전에 File
작업을 초기화합니다. 이는 몇 가지 추가 속성이 있는 Suite
의 상위 집합입니다. 모든 작업( File
포함)에서 file
속성으로 접근할 수 있습니다.
interface File extends Suite {
/**
* 파일이 속한 풀의 이름입니다.
* @default 'forks'
*/
pool?: string;
/**
* UNIX 스타일의 파일 경로입니다.
*/
filepath: string;
/**
* 파일이 속한 워크스페이스 프로젝트의 이름입니다.
*/
projectName: string | undefined;
/**
* 파일의 모든 테스트를 수집하는 데 걸린 시간입니다.
* 이 시간은 파일의 모든 종속 모듈을 임포트하는 시간도 포함합니다.
*/
collectDuration?: number;
/**
* 설정 파일을 가져오는 데 걸린 시간입니다.
*/
setupDuration?: number;
/**
* 테스트 실행 없이 파일이 초기화되었는지 여부입니다.
* 이는 Vitest에 의해 서버 측에서 상태를 채우기 위해 수행됩니다.
*/
local?: boolean;
}
모든 스위트에는 수집 단계 중에 채워지는 tasks
속성이 있습니다. 작업 트리를 위에서 아래로 탐색하는 데 유용합니다.
interface Suite extends TaskBase {
type: 'suite';
/**
* 파일 작업입니다. 파일의 루트 작업입니다.
*/
file: File;
/**
* 스위트의 일부인 작업 배열입니다.
*/
tasks: Task[];
}
모든 작업에는 작업이 위치한 스위트를 참조하는 suite
속성이 있습니다. test
또는 describe
가 최상위 수준에서 시작되면 suite
속성이 없습니다 (file
과 같지 않습니다!). File
도 suite
속성을 갖지 않습니다. 작업을 아래에서 위로 탐색하는 데 유용합니다.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* 테스트 함수에 전달될 테스트 컨텍스트입니다.
*/
context: TaskContext<Test> & ExtraContext & TestContext;
/**
* 파일 작업입니다. 파일의 루트 작업입니다.
*/
file: File;
/**
* `t.skip()` 호출로 인해 작업이 건너뛰어졌는지 여부입니다.
*/
pending?: boolean;
/**
* 작업이 실패하더라도 성공해야 하는지 여부입니다. 작업이 실패하면 통과로 표시됩니다.
*/
fails?: boolean;
/**
* 작업이 실패하면 실행될 훅입니다. 순서는 `sequence.hooks` 옵션에 따라 다릅니다.
*/
onFailed?: OnTestFailedHandler[];
/**
* 작업이 완료된 후 실행될 훅입니다. 순서는 `sequence.hooks` 옵션에 따라 다릅니다.
*/
onFinished?: OnTestFinishedHandler[];
/**
* 테스트 완료 전에 기다려야 할 비동기 기대(async expects)의 Promise를 저장합니다.
*/
promises?: Promise<any>[];
}
모든 작업에는 result
필드가 있을 수 있습니다. 스위트는 스위트 콜백 또는 beforeAll
/afterAll
콜백 내에서 발생한 오류로 인해 테스트를 수집할 수 없는 경우에만 이 필드를 가질 수 있습니다. 테스트는 콜백이 호출된 후 항상 이 필드를 가집니다. 결과에 따라 state
및 errors
필드가 존재합니다. beforeEach
또는 afterEach
콜백에서 오류가 발생하면 발생한 오류가 task.result.errors
에 존재합니다.
export interface TaskResult {
/**
* 작업의 상태입니다. 수집 중에 `task.mode`를 상속합니다.
* 작업이 완료되면 `pass` 또는 `fail`로 변경됩니다.
* - **pass**: 작업이 성공적으로 실행되었습니다.
* - **fail**: 작업이 실패했습니다.
*/
state: TaskState;
/**
* 작업 실행 중에 발생한 오류입니다.
* `expect.soft()`가 여러 번 실패한 경우 여러 오류가 발생할 수 있습니다.
*/
errors?: ErrorWithDiff[];
/**
* 작업 실행에 걸린 시간(밀리초)입니다.
*/
duration?: number;
/**
* 작업 실행이 시작된 시간(밀리초)입니다.
*/
startTime?: number;
/**
* 작업 완료 후 힙 크기(바이트)입니다.
* `logHeapUsage` 옵션이 설정되고 `process.memoryUsage`가 정의된 경우에만 사용할 수 있습니다.
*/
heap?: number;
/**
* 이 작업과 관련된 훅의 상태입니다. 보고 중에 유용합니다.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* 작업이 재시도된 횟수입니다. 작업은 실패하고 `retry` 옵션이 설정된 경우에만 재시도됩니다.
*/
retryCount?: number;
/**
* 작업이 반복된 횟수입니다. 작업은 `repeats` 옵션이 설정된 경우에만 반복됩니다. 이 숫자에는 `retryCount`도 포함됩니다.
*/
repeatCount?: number;
}
사용자 정의 작업 함수
Vitest는 사용자가 내장 리포터를 재사용할 수 있도록 Custom
작업 유형을 노출합니다. 사실상 Test
와 동일하지만 유형이 'custom'
입니다.
작업은 스위트의 일부인 객체입니다. suite.task
메서드를 사용하여 현재 스위트에 자동으로 추가됩니다.
// ./utils/custom.js
import { createTaskCollector, getCurrentSuite, setFn } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// 이 함수는 수집 단계 중에 호출됩니다.
// 여기서 함수 핸들러를 호출하지 말고,
// "getCurrentSuite().task()" 메서드를 사용하여 스위트 작업에 추가하십시오.
// 참고: createTaskCollector는 "todo"/"each"/...에 대한 지원을 제공합니다.
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // "todo"/"skip"/...가 올바르게 추적되도록 합니다.
meta: {
customPropertyToDifferentiateTask: true,
},
handler: fn,
timeout,
});
});
// ./garden/tasks.test.js
import { afterAll, beforeAll, describe, myCustomTask } from '../custom.js';
import { gardener } from './gardener.js';
describe('정원 관리', () => {
beforeAll(() => {
gardener.putWorkingClothes();
});
myCustomTask('잔디 제거', () => {
gardener.weedTheGrass();
});
myCustomTask.todo('잔디 깎기', () => {
gardener.mowerTheLawn();
});
myCustomTask('꽃에 물 주기', () => {
gardener.waterFlowers();
});
afterAll(() => {
gardener.goHome();
});
});
vitest ./garden/tasks.test.js
WARNING
사용자 정의 러너가 없거나 runTest
메서드를 정의하지 않은 경우 Vitest는 작업을 자동으로 검색하려고 시도합니다. setFn
으로 함수를 추가하지 않으면 실패합니다.