Ejecutor de Pruebas
WARNING
Esta es una API avanzada. Si solo necesitas ejecutar pruebas, es probable que no requieras esto. Su uso principal es para autores de bibliotecas.
Puedes especificar la ruta a tu ejecutor de pruebas mediante la opción runner
en tu archivo de configuración. Este archivo debe tener una exportación por defecto con un constructor de clase que implemente los siguientes métodos:
export interface VitestRunner {
/**
* Primera función que se ejecuta antes de recolectar y ejecutar las pruebas.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* Se ejecuta después de recolectar las pruebas y antes de "onBeforeRun".
*/
onCollected?: (files: File[]) => unknown;
/**
* Se invoca cuando el ejecutor de pruebas debe cancelar las próximas ejecuciones.
* El ejecutor debe estar atento a este método y marcar las pruebas y suites como omitidas en
* "onBeforeRunSuite" y "onBeforeRunTask" cuando se llama.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Se invoca antes de ejecutar una sola prueba. Aún no tiene el campo "result".
*/
onBeforeRunTest?: (test: TaskPopulated) => unknown;
/**
* Se invoca antes de ejecutar la función de prueba. Ya contiene el campo "result" con "state" y "startTime".
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Se invoca después de que se establecen el resultado y el estado.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Se invoca justo después de ejecutar la función de prueba. Todavía no tiene el nuevo estado. No se ejecutará si la función de prueba lanza una excepción.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Se invoca antes de ejecutar una sola suite. Aún no tiene el campo "result".
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Se invoca después de ejecutar una sola suite. Ya tiene estado y resultado.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Si se define, se llamará en lugar de la partición y el manejo habitual de suites de Vitest.
* Los hooks "before" y "after" se ejecutarán normalmente.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Si se define, se llamará en lugar del manejo habitual de Vitest. Útil si tienes tu propia función de tarea personalizada.
* Los hooks "before" y "after" se ejecutarán normalmente.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Se invoca cuando se actualiza una tarea. Equivale a "onTaskUpdate" en un reportero, pero se ejecuta en el mismo hilo que las pruebas.
*/
onTaskUpdate?: (task: [string, TaskResult | undefined][]) => Promise<void>;
/**
* Se invoca antes de ejecutar todas las pruebas en las rutas recolectadas.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* Se invoca justo después de ejecutar todas las pruebas en las rutas recolectadas.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* Se invoca cuando se define un nuevo contexto para una tarea. Es útil si deseas añadir propiedades personalizadas al contexto.
* Si solo deseas definir un contexto personalizado con un ejecutor, considera usar "beforeAll" en "setupFiles" en su lugar.
*
* Este método se invoca tanto para los manejadores "test" como "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>;
/**
* Se invoca cuando se importan ciertos archivos. Puede ocurrir en dos situaciones: al recolectar pruebas y al importar archivos de configuración.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Configuración disponible públicamente.
*/
config: VitestRunnerConfig;
}
Al instanciar esta clase, Vitest pasa la configuración de Vitest; debes exponerla como la propiedad config
.
WARNING
Vitest también inyecta una instancia de ViteNodeRunner
como la propiedad __vitest_executor
. Puedes usarla para procesar archivos en el método importFile
(este es el comportamiento por defecto de TestRunner
y BenchmarkRunner
).
ViteNodeRunner
expone el método executeId
, que se utiliza para importar archivos de prueba en un entorno amigable para Vite. Esto significa que resolverá las importaciones y transformará el contenido del archivo en tiempo de ejecución para que Node pueda entenderlo.
TIP
El soporte de snapshots y algunas otras características dependen del ejecutor. Si no quieres perderlo, puedes extender tu ejecutor desde VitestTestRunner
importado de vitest/runners
. También expone BenchmarkNodeRunner
, si quieres extender la funcionalidad de benchmark.
Tareas
Las suites y las pruebas se denominan tasks
internamente. El ejecutor de Vitest inicia una tarea File
antes de recolectar cualquier prueba; esta es un superconjunto de Suite
con algunas propiedades adicionales. Está disponible en cada tarea (incluyendo File
) como la propiedad file
.
interface File extends Suite {
/**
* El nombre del pool al que pertenece el archivo.
* @default 'forks'
*/
pool?: string;
/**
* La ruta al archivo en formato UNIX.
*/
filepath: string;
/**
* El nombre del proyecto del espacio de trabajo al que pertenece el archivo.
*/
projectName: string | undefined;
/**
* El tiempo que tardó en recolectar todas las pruebas en el archivo.
* Este tiempo también incluye la importación de todas las dependencias del archivo.
*/
collectDuration?: number;
/**
* El tiempo que tardó en importar el archivo de configuración.
*/
setupDuration?: number;
/**
* Si el archivo se inicia sin ejecutar ninguna prueba.
* Esto se hace para poblar el estado en el lado del servidor por parte de Vitest.
*/
local?: boolean;
}
Cada suite tiene la propiedad tasks
que se completa durante la fase de recolección. Es útil para recorrer el árbol de tareas de arriba a abajo.
interface Suite extends TaskBase {
type: 'suite';
/**
* Tarea de archivo. Es la tarea raíz del archivo.
*/
file: File;
/**
* Un array de tareas que forman parte de la suite.
*/
tasks: Task[];
}
Cada tarea tiene la propiedad suite
que hace referencia a la suite a la que pertenece. Si test
o describe
se inician en el nivel superior, no tendrán la propiedad suite
(¡no será igual a file
!). File
tampoco tiene nunca la propiedad suite
. Es útil para recorrer las tareas de abajo a arriba.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Contexto de la prueba que se pasará a la función de prueba.
*/
context: TaskContext<Test> & ExtraContext & TestContext;
/**
* Tarea de archivo. Es la tarea raíz del archivo.
*/
file: File;
/**
* Si la tarea se omitió al llamar a `t.skip()`.
*/
pending?: boolean;
/**
* Si la tarea debe considerarse exitosa aunque falle. Si la tarea falla, se marcará como pasada.
*/
fails?: boolean;
/**
* Hooks que se ejecutarán si la tarea falla. El orden depende de la opción `sequence.hooks`.
*/
onFailed?: OnTestFailedHandler[];
/**
* Hooks que se ejecutarán después de que la tarea termine. El orden depende de la opción `sequence.hooks`.
*/
onFinished?: OnTestFinishedHandler[];
/**
* Guarda promesas (de expects asíncronos) para esperarlas antes de terminar la prueba.
*/
promises?: Promise<any>[];
}
Cada tarea puede tener el campo result
. Las suites solo pueden tener este campo si un error lanzado dentro de un callback de suite o callbacks beforeAll
/afterAll
les impide recolectar pruebas. Las pruebas siempre tienen este campo después de que se llaman sus callbacks; los campos state
y errors
se establecen según el resultado. Si se lanzó un error en los callbacks beforeEach
o afterEach
, el error lanzado estará presente en task.result.errors
.
export interface TaskResult {
/**
* Estado de la tarea. Hereda el `task.mode` durante la recolección.
* Cuando la tarea ha terminado, cambiará a `pass` o `fail`.
* - **pass**: la tarea se ejecutó correctamente
* - **fail**: la tarea falló
*/
state: TaskState;
/**
* Errores que ocurrieron durante la ejecución de la tarea. Es posible tener varios errores
* si `expect.soft()` falló varias veces.
*/
errors?: ErrorWithDiff[];
/**
* Cuánto tiempo en milisegundos tardó la tarea en ejecutarse.
*/
duration?: number;
/**
* Tiempo en milisegundos cuando la tarea comenzó a ejecutarse.
*/
startTime?: number;
/**
* Tamaño del heap en bytes después de que la tarea terminó.
* Solo disponible si la opción `logHeapUsage` está configurada y `process.memoryUsage` está definido.
*/
heap?: number;
/**
* Estado de los hooks relacionados con esta tarea. Es útil durante la generación de informes.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* Número de veces que se reintentó la tarea. La tarea solo se reintenta si
* falló y la opción `retry` está configurada.
*/
retryCount?: number;
/**
* La cantidad de veces que se repitió la tarea. La tarea se repite solo si
* la opción `repeats` está configurada. Este valor incluye también `retryCount`.
*/
repeatCount?: number;
}
Tu Función de Tarea
Vitest expone un tipo de tarea Custom
que permite a los usuarios reutilizar los reporteros integrados. Es virtualmente idéntico a Test
, pero tiene el tipo 'custom'
.
Una tarea es un objeto que forma parte de una suite. Se agrega automáticamente a la suite actual con el método suite.task
:
// ./utils/custom.js
import { createTaskCollector, getCurrentSuite, setFn } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// esta función se llamará durante la fase de recolección:
// no llames al manejador de función aquí, añádelo a las tareas de la suite
// con el método "getCurrentSuite().task()"
// nota: createTaskCollector proporciona soporte para "todo"/"each"/...
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // para que "todo"/"skip"/... se rastree correctamente
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
Si no tienes un ejecutor personalizado o no definiste el método runTask
, Vitest intentará recuperar una tarea automáticamente. Si no añadiste una función con setFn
, fallará.