테스트 컨텍스트
Playwright Fixtures에서 영감을 받은 Vitest의 테스트 컨텍스트를 사용하면 테스트에서 활용할 유틸리티, 상태 및 픽스처를 정의할 수 있습니다.
사용법
각 테스트 콜백 함수의 첫 번째 인수는 테스트 컨텍스트입니다.
import { it } from 'vitest';
it('should work', ({ task }) => {
// 테스트 이름을 출력합니다.
console.log(task.name);
});
내장 테스트 컨텍스트
task
테스트에 대한 메타데이터를 포함하는 읽기 전용 객체입니다.
expect
현재 테스트에 연결된 expect
API:
import { it } from 'vitest';
it('math is easy', ({ expect }) => {
expect(2 + 2).toBe(4);
});
이 API는 전역 expect
가 스냅샷 테스트를 올바르게 추적하지 못하기 때문에 스냅샷 테스트를 동시에 실행하는 데 유용합니다.
import { it } from 'vitest';
it.concurrent('math is easy', ({ expect }) => {
expect(2 + 2).toMatchInlineSnapshot();
});
it.concurrent('math is hard', ({ expect }) => {
expect(2 * 2).toMatchInlineSnapshot();
});
skip
function skip(note?: string): never;
function skip(condition: boolean, note?: string): void;
이후 테스트 실행을 건너뛰고, 해당 테스트를 건너뛴 상태로 표시합니다.
import { expect, it } from 'vitest';
it('math is hard', ({ skip }) => {
skip();
expect(2 + 2).toBe(5);
});
Vitest 3.1부터는 조건부로 테스트를 건너뛰기 위해 불리언 매개변수를 허용합니다.
it('math is hard', ({ skip, mind }) => {
skip(mind === 'foggy');
expect(2 + 2).toBe(5);
});
annotate
3.2.0+
function annotate(
message: string,
attachment?: TestAttachment
): Promise<TestAnnotation>;
function annotate(
message: string,
type?: string,
attachment?: TestAttachment
): Promise<TestAnnotation>;
test('annotations API', async ({ annotate }) => {
await annotate('https://github.com/vitest-dev/vitest/pull/7953', 'issues');
});
signal
3.2.0+
Vitest에 의해 중단될 수 있는 AbortSignal
입니다. 신호는 다음과 같은 상황에서 중단됩니다.
- 테스트 시간 초과
- 사용자가 Ctrl+C로 테스트 실행을 수동으로 취소함
vitest.cancelCurrentRun
이 프로그래밍 방식으로 호출됨- 다른 테스트가 병렬로 실패하고
bail
플래그가 설정됨
it('stop request when test times out', async ({ signal }) => {
await fetch('/resource', { signal });
}, 2000);
onTestFailed
현재 테스트에 연결된 onTestFailed
훅입니다. 이 API는 테스트를 동시에 실행할 때, 특정 테스트에 대해 특별한 처리가 필요할 경우 유용합니다.
onTestFinished
현재 테스트에 연결된 onTestFinished
훅입니다. 이 API는 테스트를 동시에 실행할 때, 특정 테스트에 대해 특별한 처리가 필요할 경우 유용합니다.
테스트 컨텍스트 확장
Vitest는 테스트 컨텍스트를 확장하는 두 가지 방식을 제공합니다.
test.extend
Playwright와 마찬가지로 이 메서드를 사용하여 사용자 지정 픽스처로 자신만의 test
API를 정의하고, 이를 어디서든 재사용할 수 있습니다.
예를 들어, 먼저 todos
와 archive
라는 두 개의 픽스처를 사용하여 test
객체를 생성합니다.
import { test as baseTest } from 'vitest';
const todos = [];
const archive = [];
export const test = baseTest.extend({
todos: async ({}, use) => {
// 각 테스트 함수 전에 픽스처를 준비합니다.
todos.push(1, 2, 3);
// 픽스처 값을 사용합니다.
await use(todos);
// 각 테스트 함수 후에 픽스처를 정리합니다.
todos.length = 0;
},
archive,
});
그런 다음 가져와서 사용할 수 있습니다.
import { expect } from 'vitest';
import { test } from './my-test.js';
test('add items to todos', ({ todos }) => {
expect(todos.length).toBe(3);
todos.push(4);
expect(todos.length).toBe(4);
});
test('move items from todos to archive', ({ todos, archive }) => {
expect(todos.length).toBe(3);
expect(archive.length).toBe(0);
archive.push(todos.pop());
expect(todos.length).toBe(2);
expect(archive.length).toBe(1);
});
또한 test
를 확장하여 더 많은 픽스처를 추가하거나 기존 픽스처를 재정의할 수 있습니다.
import { test as todosTest } from './my-test.js';
export const test = todosTest.extend({
settings: {
// ...
},
});
픽스처 초기화
Vitest 러너는 사용량에 따라 픽스처를 스마트하게 초기화하고 테스트 컨텍스트에 주입합니다.
import { test as baseTest } from 'vitest';
const test = baseTest.extend<{
todos: number[];
archive: number[];
}>({
todos: async ({ task }, use) => {
await use([1, 2, 3]);
},
archive: [],
});
// todos는 실행되지 않습니다.
test('skip', () => {});
test('skip', ({ archive }) => {});
// todos는 실행됩니다.
test('run', ({ todos }) => {});
WARNING
test.extend()
를 픽스처와 함께 사용할 때는 픽스처 함수와 테스트 함수 모두에서 { todos }
와 같은 객체 비구조화 패턴을 사용하여 컨텍스트에 접근해야 합니다.
test('context must be destructured', (context) => {
expect(context.todos.length).toBe(2)
})
test('context must be destructured', ({ todos }) => {
expect(todos.length).toBe(2)
})
자동 픽스처
Vitest는 픽스처에 대한 튜플 구문도 지원하며, 각 픽스처에 대한 옵션을 전달할 수 있습니다. 예를 들어, 테스트에서 사용되지 않더라도 픽스처를 명시적으로 초기화하는 데 사용할 수 있습니다.
import { test as base } from 'vitest';
const test = base.extend({
fixture: [
async ({}, use) => {
// 이 함수가 실행됩니다.
setup();
await use();
teardown();
},
{ auto: true }, // 자동 픽스처로 표시
],
});
test('works correctly');
기본 픽스처
Vitest 3부터는 다른 프로젝트에서 다른 값을 제공할 수 있습니다. 이 기능을 활성화하려면 옵션에 { injected: true }
를 전달해야 합니다. 프로젝트 구성에 키가 지정되지 않은 경우 기본값이 사용됩니다.
import { test as base } from 'vitest';
const test = base.extend({
url: [
// 구성에 "url"이 정의되지 않은 경우 기본값
'/default',
// 재정의를 허용하도록 픽스처를 "주입 가능"으로 표시
{ injected: true },
],
});
test('works correctly', ({ url }) => {
// "project-new"에서 url은 "/default"입니다.
// "project-full"에서 url은 "/full"입니다.
// "project-empty"에서 url은 "/empty"입니다.
});
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
projects: [
{
test: {
name: 'project-new',
},
},
{
test: {
name: 'project-full',
provide: {
url: '/full',
},
},
},
{
test: {
name: 'project-empty',
provide: {
url: '/empty',
},
},
},
],
},
});
스위트 범위 지정 값 3.1.0+
Vitest 3.1부터 test.scoped
API를 사용하여 스위트 및 해당 하위 항목별로 컨텍스트 값을 재정의할 수 있습니다.
import { test as baseTest, describe, expect } from 'vitest';
const test = baseTest.extend({
dependency: 'default',
dependant: ({ dependency }, use) => use({ dependency }),
});
describe('use scoped values', () => {
test.scoped({ dependency: 'new' });
test('uses scoped value', ({ dependant }) => {
// `dependant`는 이 스위트의 모든 테스트에 범위가 지정된
// 새로 재정의된 값을 사용합니다.
expect(dependant).toEqual({ dependency: 'new' });
});
describe('keeps using scoped value', () => {
test('uses scoped value', ({ dependant }) => {
// 중첩된 스위트가 값을 상속했습니다.
expect(dependant).toEqual({ dependency: 'new' });
});
});
});
test('keep using the default values', ({ dependant }) => {
// `dependency`는 .scoped가 있는 스위트 외부에서
// 기본값을 사용합니다.
expect(dependant).toEqual({ dependency: 'default' });
});
이 API는 데이터베이스 연결과 같은 동적 변수에 의존하는 컨텍스트 값이 있는 경우 특히 유용합니다.
const test = baseTest.extend<{
db: Database;
schema: string;
}>({
db: async ({ schema }, use) => {
const db = await createDb({ schema });
await use(db);
await cleanup(db);
},
schema: '',
});
describe('one type of schema', () => {
test.scoped({ schema: 'schema-1' });
// ... 테스트
});
describe('another type of schema', () => {
test.scoped({ schema: 'schema-2' });
// ... 테스트
});
범위별 컨텍스트 3.2.0+
파일 또는 워커당 한 번 시작되는 컨텍스트를 정의할 수 있습니다. 객체 매개변수를 통해 일반 픽스처와 동일한 방식으로 시작됩니다.
import { test as baseTest } from 'vitest';
export const test = baseTest.extend({
perFile: [({}, { use }) => use([]), { scope: 'file' }],
perWorker: [({}, { use }) => use([]), { scope: 'worker' }],
});
값은 픽스처 옵션에 auto: true
가 없는 한, 테스트가 처음 접근했을 때 초기화됩니다. 이 경우 값은 어떤 테스트가 실행되기 전에 초기화됩니다.
const test = baseTest.extend({
perFile: [
({}, { use }) => use([]),
{
scope: 'file',
// 어떤 테스트 전에 항상 이 훅을 실행합니다.
auto: true,
},
],
});
worker
범위는 워커당 한 번 픽스처를 실행합니다. 실행 중인 워커의 수는 다양한 요인에 따라 달라집니다. 기본적으로 모든 파일은 별도의 워커에서 실행되므로 file
및 worker
범위는 동일하게 작동합니다.
그러나 격리를 비활성화하면 워커 수는 maxWorkers
또는 poolOptions
구성에 의해 제한됩니다.
vmThreads
또는 vmForks
에서 테스트를 실행할 때 scope: 'worker'
를 지정하면 scope: 'file'
과 동일하게 작동합니다. 이 제한은 모든 테스트 파일에 자체 VM 컨텍스트가 있기 때문에 Vitest가 한 번만 초기화하면 한 컨텍스트의 내용이 다른 컨텍스트로 유출되어 많은 참조 불일치(예: 동일한 클래스의 인스턴스가 다른 생성자를 참조)를 생성할 수 있기 때문에 존재합니다.
TypeScript
모든 사용자 지정 컨텍스트에 대한 픽스처 유형을 제공하려면 픽스처 유형을 제네릭으로 전달할 수 있습니다.
interface MyFixtures {
todos: number[];
archive: number[];
}
const test = baseTest.extend<MyFixtures>({
todos: [],
archive: [],
});
test('types are defined correctly', ({ todos, archive }) => {
expectTypeOf(todos).toEqualTypeOf<number[]>();
expectTypeOf(archive).toEqualTypeOf<number[]>();
});
타입 추론
use
함수가 호출될 때 Vitest는 타입 추론을 지원하지 않습니다. test.extend
가 호출될 때 전체 컨텍스트 타입을 제네릭 타입으로 전달하는 것이 좋습니다.
import { test as baseTest } from 'vitest';
const test = baseTest.extend<{
todos: number[];
schema: string;
}>({
todos: ({ schema }, use) => use([]),
schema: 'test',
});
test('types are correct', ({
todos, // number[]
schema, // string
}) => {
// ...
});
beforeEach
및 afterEach
Deprecated
이것은 컨텍스트를 확장하는 구식 방법이며 test
가 test.extend
로 확장될 때는 작동하지 않습니다.
컨텍스트는 각 테스트마다 다릅니다. beforeEach
및 afterEach
훅 내에서 컨텍스트에 접근하고 확장할 수 있습니다.
import { beforeEach, it } from 'vitest';
beforeEach(async context => {
// 컨텍스트 확장
context.foo = 'bar';
});
it('should work', ({ foo }) => {
console.log(foo); // 'bar'
});
TypeScript
모든 사용자 지정 컨텍스트에 대한 속성 유형을 제공하려면 TestContext
유형을 다음과 같이 확장할 수 있습니다.
declare module 'vitest' {
export interface TestContext {
foo?: string;
}
}
특정 beforeEach
, afterEach
, it
및 test
훅에 대해서만 속성 유형을 제공하려면 유형을 제네릭으로 전달할 수 있습니다.
interface LocalTestContext {
foo: string;
}
beforeEach<LocalTestContext>(async context => {
// context의 타입은 'TestContext & LocalTestContext'입니다.
context.foo = 'bar';
});
it<LocalTestContext>('should work', ({ foo }) => {
// foo의 타입은 'string'입니다.
console.log(foo); // 'bar'
});