Исполнитель тестов
WARNING
Это продвинутый 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;
/**
* Вызывается при определении нового контекста для теста. Полезно, если вы хотите добавить собственные свойства в контекст.
* Если вы хотите определить собственный контекст только с помощью исполнителя, рассмотрите возможность использования "beforeAll" в "setupFiles".
*
* Этот метод вызывается как для обработчиков "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
Поддержка снимков и некоторые другие функции зависят от исполнителя. Если вы не хотите их потерять, вы можете унаследовать свой исполнитель от VitestTestRunner
, импортированного из vitest/runners
. Он также предоставляет доступ к BenchmarkNodeRunner
, если вы хотите расширить функциональность бенчмарков.
Задачи
Наборы (Suites) и тесты внутренне называются 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[];
/**
* Хранит промисы (например, из асинхронных expect), чтобы дождаться их перед завершением теста.
*/
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('take care of the garden', () => {
beforeAll(() => {
gardener.putWorkingClothes();
});
myCustomTask('weed the grass', () => {
gardener.weedTheGrass();
});
myCustomTask.todo('mow the lawn', () => {
gardener.mowerTheLawn();
});
myCustomTask('water flowers', () => {
gardener.waterFlowers();
});
afterAll(() => {
gardener.goHome();
});
});
vitest ./garden/tasks.test.js
WARNING
Если у вас нет собственного исполнителя или вы не определили метод runTest
, Vitest попытается получить задачу автоматически. Если вы не добавили функцию с помощью setFn
, это приведет к ошибке.