Runner testów
WARNING
To jest zaawansowane API. Jeśli chcesz po prostu uruchomić testy, prawdopodobnie nie potrzebujesz tego. Jest ono używane głównie przez autorów bibliotek.
Możesz określić ścieżkę do swojego runnera testów za pomocą opcji runner
w pliku konfiguracyjnym. Ten plik powinien mieć domyślny eksport, który zawiera konstruktor klasy implementujący następujące metody:
export interface VitestRunner {
/**
* Pierwsza metoda, która jest wywoływana przed zbieraniem i uruchamianiem testów.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* Wywoływana po zebraniu testów i przed "onBeforeRun".
*/
onCollected?: (files: File[]) => unknown;
/**
* Wywoływana, gdy runner testów powinien anulować następne uruchomienia testów.
* Runner powinien nasłuchiwać tej metody i oznaczać testy i zestawy jako pominięte w
* "onBeforeRunSuite" i "onBeforeRunTask" po wywołaniu.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Wywoływana przed uruchomieniem pojedynczego testu. Pole "result" nie jest jeszcze dostępne.
*/
onBeforeRunTest?: (test: TaskPopulated) => unknown;
/**
* Wywoływana przed faktycznym uruchomieniem funkcji testowej. Pole "result" jest już dostępne wraz z polami "state" i "startTime".
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Wywoływana po ustawieniu pól "result" i "state".
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Wywoływana zaraz po uruchomieniu funkcji testowej. Nowy stan nie jest jeszcze dostępny. Nie zostanie wywołana, jeśli funkcja testowa zgłosi błąd.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Wywoływana przed uruchomieniem pojedynczego zestawu testów. Pole "result" nie jest jeszcze dostępne.
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Wywoływana po uruchomieniu pojedynczego zestawu. Pola "state" i "result" są dostępne.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Jeśli jest zdefiniowana, zostanie wywołana zamiast standardowego podziału i obsługi zestawów przez Vitest.
* Hooki "before" i "after" nie będą ignorowane.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Jeśli jest zdefiniowana, zostanie wywołana zamiast standardowej obsługi przez Vitest. Przydatne, jeśli masz własną funkcję testową.
* Hooki "before" i "after" nie będą ignorowane.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Wywoływana, gdy zadanie jest aktualizowane. Działa tak samo jak "onTaskUpdate" w reporterze, ale w tym samym wątku co testy.
*/
onTaskUpdate?: (task: [string, TaskResult | undefined][]) => Promise<void>;
/**
* Wywoływana przed uruchomieniem wszystkich testów w zebranych ścieżkach.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* Wywoływana zaraz po uruchomieniu wszystkich testów w zebranych ścieżkach.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* Wywoływana, gdy zdefiniowany jest nowy kontekst dla testu. Przydatne, jeśli chcesz dodać niestandardowe właściwości do kontekstu.
* Jeśli chcesz tylko zdefiniować niestandardowy kontekst za pomocą runnera, rozważ użycie "beforeAll" w "setupFiles" zamiast tej metody.
*
* Ta metoda jest wywoływana zarówno dla handlerów "test", jak i "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>;
/**
* Wywoływana, gdy importowane są określone pliki. Może być wywołana w dwóch sytuacjach: podczas zbierania testów i podczas importowania plików konfiguracyjnych.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Publicznie dostępna konfiguracja.
*/
config: VitestRunnerConfig;
}
Podczas inicjowania tej klasy, Vitest przekazuje konfigurację Vitest - powinieneś ją udostępnić jako właściwość config
.
WARNING
Vitest wstrzykuje również instancję ViteNodeRunner
jako właściwość __vitest_executor
. Możesz jej użyć do przetwarzania plików w metodzie importFile
(jest to domyślne zachowanie TestRunner
i BenchmarkRunner
).
ViteNodeRunner
udostępnia metodę executeId
, która służy do importowania plików testowych w środowisku przyjaznym dla Vite. Oznacza to, że rozwiąże importy i przekształci zawartość pliku w czasie wykonania, aby Node mógł ją zrozumieć.
TIP
Obsługa snapshotów i niektóre inne funkcje zależą od runnera. Jeśli nie chcesz ich stracić, możesz rozszerzyć swój runner z VitestTestRunner
importowanego z vitest/runners
. Udostępnia on również BenchmarkNodeRunner
, jeśli chcesz rozszerzyć funkcjonalność benchmarków.
Zadania
Zestawy i testy są wewnętrznie określane jako tasks
. Runner Vitest inicjuje zadanie File
przed zebraniem jakichkolwiek testów - jest to nadzbiór Suite
, który zawiera kilka dodatkowych właściwości. Jest dostępne w każdym zadaniu (w tym File
) jako właściwość file
.
interface File extends Suite {
/**
* Nazwa puli, do której należy plik.
* @default 'forks'
*/
pool?: string;
/**
* Ścieżka do pliku w formacie UNIX.
*/
filepath: string;
/**
* Nazwa projektu obszaru roboczego, do którego należy plik.
*/
projectName: string | undefined;
/**
* Czas, jaki zajęło zebranie wszystkich testów w pliku.
* Ten czas obejmuje również importowanie wszystkich zależności pliku.
*/
collectDuration?: number;
/**
* Czas, jaki zajęło zaimportowanie pliku konfiguracyjnego.
*/
setupDuration?: number;
/**
* Czy plik jest inicjowany bez uruchamiania żadnych testów.
* Odbywa się to w celu wypełnienia stanu po stronie serwera przez Vitest.
*/
local?: boolean;
}
Każdy zestaw ma właściwość tasks
, która jest wypełniana w trakcie fazy zbierania. Jest to przydatne do przeglądania drzewa zadań od góry do dołu.
interface Suite extends TaskBase {
type: 'suite';
/**
* Zadanie pliku. Jest to zadanie główne pliku.
*/
file: File;
/**
* Tablica zadań, które są częścią zestawu.
*/
tasks: Task[];
}
Każde zadanie ma właściwość suite
, która odwołuje się do zestawu, w którym się znajduje. Jeśli test
lub describe
są inicjowane na najwyższym poziomie, nie będą miały właściwości suite
(nie będzie ona równa file
!). File
również nigdy nie ma właściwości suite
. Jest to przydatne do przeglądania zadań od dołu do góry.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Kontekst testu, który zostanie przekazany do funkcji testowej.
*/
context: TaskContext<Test> & ExtraContext & TestContext;
/**
* Zadanie pliku. Jest to zadanie główne pliku.
*/
file: File;
/**
* Czy zadanie zostało pominięte przez wywołanie `t.skip()`.
*/
pending?: boolean;
/**
* Czy zadanie powinno zakończyć się sukcesem, jeśli się nie powiedzie. Jeśli zadanie się nie powiedzie, zostanie oznaczone jako zakończone sukcesem.
*/
fails?: boolean;
/**
* Hooki, które zostaną uruchomione, jeśli zadanie się nie powiedzie. Kolejność zależy od opcji `sequence.hooks`.
*/
onFailed?: OnTestFailedHandler[];
/**
* Hooki, które zostaną uruchomione po zakończeniu zadania. Kolejność zależy od opcji `sequence.hooks`.
*/
onFinished?: OnTestFinishedHandler[];
/**
* Przechowuje obietnice (z asynchronicznych oczekiwań), aby poczekać na nie przed zakończeniem testu
*/
promises?: Promise<any>[];
}
Każde zadanie może zawierać pole result
. Zestawy mogą mieć to pole tylko wtedy, gdy błąd zgłoszony w wywołaniach zwrotnych zestawu lub w beforeAll
/afterAll
uniemożliwia im zbieranie testów. Testy zawsze mają to pole po wywołaniu ich wywołań zwrotnych; pola state
i errors
są obecne w zależności od wyniku. Jeśli błąd został zgłoszony w wywołaniach zwrotnych beforeEach
lub afterEach
, zgłoszony błąd będzie dostępny w task.result.errors
.
export interface TaskResult {
/**
* Stan zadania, który dziedziczy `task.mode` podczas zbierania.
* Gdy zadanie zostanie zakończone, zmieni się na `pass` lub `fail`.
* - **pass**: zadanie zakończone sukcesem
* - **fail**: zadanie nie powiodło się
*/
state: TaskState;
/**
* Błędy, które wystąpiły podczas wykonywania zadania; możliwe jest wystąpienie kilku błędów
* jeśli `expect.soft()` nie powiodło się wielokrotnie.
*/
errors?: ErrorWithDiff[];
/**
* Czas w milisekundach, który zajęło uruchomienie zadania.
*/
duration?: number;
/**
* Czas w milisekundach, w którym zadanie rozpoczęło się.
*/
startTime?: number;
/**
* Rozmiar sterty w bajtach po zakończeniu zadania; dostępne tylko, jeśli opcja `logHeapUsage` jest ustawiona.
* Only available if `logHeapUsage` option is set and `process.memoryUsage` is defined.
*/
heap?: number;
/**
* Stan hooków związanych z tym zadaniem; przydatne podczas raportowania.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* Liczba razy, gdy zadanie zostało ponowione; zadanie jest ponawiane tylko, jeśli
* nie powiodło się i opcja `retry` jest ustawiona.
*/
retryCount?: number;
/**
* Liczba razy, gdy zadanie zostało powtórzone; zadanie jest powtarzane tylko, jeśli
* opcja `repeats` jest ustawiona. Ta liczba zawiera również `retryCount`.
*/
repeatCount?: number;
}
Twoja funkcja zadania
Vitest udostępnia typ zadania Custom
, który umożliwia użytkownikom ponowne wykorzystanie wbudowanych reporterów. Jest on praktycznie taki sam jak Test
, ale ma typ 'custom'
.
Zadanie to obiekt, który należy do zestawu. Jest automatycznie dodawany do bieżącego zestawu za pomocą metody suite.task
:
// ./utils/custom.js
import { createTaskCollector, getCurrentSuite, setFn } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// Ta funkcja zostanie wywołana podczas fazy zbierania:
// Nie wywołuj tutaj handlera funkcji; dodaj go do zadań zestawu
// za pomocą metody "getCurrentSuite().task()"
// Uwaga: `createTaskCollector` zapewnia wsparcie dla "todo", "each" itd.
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // aby "todo"/"skip"/... było poprawnie śledzone
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
Jeśli nie masz niestandardowego runnera lub nie zdefiniowałeś metody runTest
, Vitest spróbuje automatycznie pobrać zadanie. Jeśli nie dodałeś funkcji za pomocą setFn
, zakończy się to niepowodzeniem.