Test Runner
WARNING
Questa è un'API avanzata. Se vuoi solo eseguire i test, probabilmente non ne hai bisogno. È utilizzata principalmente dagli autori di librerie.
Puoi specificare un percorso per il tuo test runner con l'opzione runner
nel tuo file di configurazione. Questo file dovrebbe esportare di default un costruttore di classe che implementa questi metodi:
export interface VitestRunner {
/**
* La prima cosa che viene chiamata prima di raccogliere ed eseguire effettivamente i test.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* Chiamato dopo aver raccolto i test e prima di "onBeforeRun".
*/
onCollected?: (files: File[]) => unknown;
/**
* Chiamato quando il test runner dovrebbe annullare le prossime esecuzioni di test.
* Il runner dovrebbe intercettare questo metodo e contrassegnare test e suite come saltati in
* "onBeforeRunSuite" e "onBeforeRunTask" quando chiamato.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Chiamato prima di eseguire un singolo test. Non possiede ancora "result".
*/
onBeforeRunTest?: (test: TaskPopulated) => unknown;
/**
* Chiamato prima di eseguire effettivamente la funzione di test. Possiede già "result" con "state" e "startTime".
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Chiamato dopo che result e state sono stati impostati.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Chiamato immediatamente dopo l'esecuzione della funzione di test. Non ha ancora il nuovo stato. Non viene chiamato se la funzione di test genera un errore.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Chiamato prima di eseguire una singola suite. Non possiede ancora "result".
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Chiamato dopo aver eseguito una singola suite. Possiede state e result.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Se definito, sostituirà la normale partizione e gestione delle suite di Vitest.
* Gli hook "before" e "after" non verranno ignorati.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Se definito, verrà chiamato al posto della normale gestione di Vitest. Utile se hai la tua funzione di test personalizzata.
* Gli hook "before" e "after" non verranno ignorati.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Chiamato quando un task viene aggiornato. Equivale a "onTaskUpdate" in un reporter, ma questo viene eseguito nello stesso thread dei test.
*/
onTaskUpdate?: (task: [string, TaskResult | undefined][]) => Promise<void>;
/**
* Chiamato prima di eseguire tutti i test nei percorsi raccolti.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* Chiamato subito dopo aver eseguito tutti i test nei percorsi raccolti.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* Chiamato quando viene definito un nuovo contesto per un test. Utile se vuoi aggiungere proprietà personalizzate al contesto.
* Se vuoi solo definire un contesto personalizzato con un runner, considera di usare "beforeAll" in "setupFiles" invece.
*
* Questo metodo viene chiamato sia per i gestori "test" che "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>;
/**
* Chiamato quando vengono importati determinati file. Può essere chiamato in due situazioni: quando si raccolgono i test e quando si importano i file di setup.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Configurazione disponibile pubblicamente.
*/
config: VitestRunnerConfig;
}
Quando si inizializza questa classe, Vitest passa la configurazione di Vitest - dovresti esporla come proprietà config
.
WARNING
Vitest inietta anche un'istanza di ViteNodeRunner
come proprietà __vitest_executor
. Puoi usarla per elaborare i file nel metodo importFile
(questo è il comportamento predefinito di TestRunner
e BenchmarkRunner
).
ViteNodeRunner
espone il metodo executeId
, che viene utilizzato per importare i file di test in un ambiente compatibile con Vite. Ciò significa che risolverà gli import e trasformerà il contenuto del file in fase di runtime in modo che Node possa comprenderlo.
TIP
Il supporto per gli snapshot e alcune altre funzionalità dipendono dal runner. Se non vuoi perderle, puoi estendere il tuo runner da VitestTestRunner
importato da vitest/runners
. Espone anche BenchmarkNodeRunner
, se vuoi estendere la funzionalità di benchmark.
Task
Suite e test sono chiamati internamente tasks
. Il runner di Vitest inizializza un task File
prima di raccogliere qualsiasi test - questo è un superset di Suite
con alcune proprietà aggiuntive. È disponibile su ogni task (incluso File
) come proprietà file
.
interface File extends Suite {
/**
* Il nome del pool a cui appartiene il file.
* @default 'forks'
*/
pool?: string;
/**
* Il percorso del file in formato UNIX.
*/
filepath: string;
/**
* Il nome del progetto workspace a cui appartiene il file.
*/
projectName: string | undefined;
/**
* Il tempo impiegato per raccogliere tutti i test nel file.
* Questo tempo include anche l'importazione di tutte le dipendenze del file.
*/
collectDuration?: number;
/**
* Il tempo impiegato per importare il file di setup.
*/
setupDuration?: number;
/**
* Indica se il file è stato inizializzato senza eseguire alcun test.
* Questa operazione viene eseguita da Vitest per popolare lo stato lato server.
*/
local?: boolean;
}
Ogni suite ha una proprietà tasks
che viene popolata durante la fase di raccolta. È utile per attraversare l'albero dei task dall'alto verso il basso.
interface Suite extends TaskBase {
type: 'suite';
/**
* Task File. È il task radice del file.
*/
file: File;
/**
* Un array di task che fanno parte della suite.
*/
tasks: Task[];
}
Ogni task ha una proprietà suite
che fa riferimento alla suite in cui si trova. Se test
o describe
vengono inizializzati al livello superiore, non avranno una proprietà suite
(non sarà uguale a file
!). Anche File
non ha mai una proprietà suite
. È utile attraversare i task dal basso verso l'alto.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Contesto del test che verrà passato alla funzione di test.
*/
context: TaskContext<Test> & ExtraContext & TestContext;
/**
* Task File. È il task radice del file.
*/
file: File;
/**
* Specifica se il task è stato saltato tramite `t.skip()`.
*/
pending?: boolean;
/**
* Indica se il task dovrebbe avere successo se fallisce. Se il task fallisce, sarà considerato superato.
*/
fails?: boolean;
/**
* Gli hook che verranno eseguiti se il task fallisce. L'ordine dipende dall'opzione `sequence.hooks`.
*/
onFailed?: OnTestFailedHandler[];
/**
* Gli hook che verranno eseguiti dopo che il task è terminato. L'ordine dipende dall'opzione `sequence.hooks`.
*/
onFinished?: OnTestFinishedHandler[];
/**
* Memorizza le promise (da expect asincroni) per attenderle prima di terminare il test
*/
promises?: Promise<any>[];
}
Ogni task può avere un campo result
. Le suite possono avere questo campo solo se un errore lanciato all'interno di un callback di suite o di callback beforeAll
/afterAll
impedisce loro di raccogliere i test. I test hanno sempre questo campo dopo che i loro callback sono stati chiamati - i campi state
e errors
vengono popolati in base all'esito. Se un errore è stato lanciato nei callback beforeEach
o afterEach
, l'errore lanciato sarà presente in task.result.errors
.
export interface TaskResult {
/**
* Stato del task. Eredita il `task.mode` durante la raccolta.
* Quando il task è terminato, cambierà in `pass` o `fail`.
* - **pass**: task eseguito con successo
* - **fail**: task fallito
*/
state: TaskState;
/**
* Errori che si sono verificati durante l'esecuzione del task. È possibile avere diversi errori
* se `expect.soft()` fallisce più volte.
*/
errors?: ErrorWithDiff[];
/**
* Quanto tempo in millisecondi ha impiegato il task per essere eseguito.
*/
duration?: number;
/**
* Tempo in millisecondi in cui il task ha iniziato l'esecuzione.
*/
startTime?: number;
/**
* Dimensione dell'heap in byte dopo che il task è terminato.
* Disponibile solo se l'opzione `logHeapUsage` è impostata e `process.memoryUsage` è definito.
*/
heap?: number;
/**
* Stato degli hook correlati a questo task. Utile durante il reporting.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* Il numero di volte in cui il task è stato ritentato. Il task viene ritentato solo se
* è fallito e l'opzione `retry` è impostata.
*/
retryCount?: number;
/**
* Il numero di volte che il task è stato ripetuto. Il task viene ripetuto solo se
* l'opzione `repeats` è impostata. Questo numero include anche `retryCount`.
*/
repeatCount?: number;
}
La tua funzione Task
Vitest offre un tipo di task Custom
che permette agli utenti di riutilizzare i reporter integrati. È praticamente identico a Test
, ma ha un tipo 'custom'
.
Un task è un oggetto che fa parte di una suite. Viene automaticamente aggiunto alla suite corrente con il metodo suite.task
:
// ./utils/custom.js
import { createTaskCollector, getCurrentSuite, setFn } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// questa funzione verrà chiamata durante la fase di raccolta:
// non chiamare qui il gestore della funzione, ma aggiungilo ai task della suite
// con il metodo "getCurrentSuite().task()"
// nota: createTaskCollector fornisce supporto per "todo"/"each"/...
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // affinché "todo"/"skip"/... vengano tracciati correttamente
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
Se non hai un runner personalizzato o non hai definito il metodo runTest
, Vitest cercherà di recuperare un task automaticamente. Se non hai aggiunto una funzione con setFn
, fallirà.