API modułu uruchamiającego
WARNING
Jest to zaawansowane API. Jeśli chcesz po prostu uruchomić testy, prawdopodobnie nie będziesz go potrzebować. Jest ono używane głównie przez autorów bibliotek.
Możesz określić ścieżkę do swojego modułu uruchamiającego testy za pomocą opcji runner
w pliku konfiguracyjnym. Ten plik powinien eksportować domyślnie klasę, która implementuje następujące metody:
export interface VitestRunner {
/**
* Pierwsza metoda wywoływana przed faktycznym 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 moduł uruchamiający testy powinien anulować kolejne uruchomienia.
* Moduł uruchamiający powinien reagować na wywołanie tej metody i oznaczać testy oraz zestawy jako pominięte w
* `onBeforeRunSuite` i `onBeforeRunTask`.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Wywoływana przed uruchomieniem pojedynczego zadania testowego. Nie posiada jeszcze właściwości `result`.
*/
onBeforeRunTask?: (test: TaskPopulated) => unknown;
/**
* Wywoływana przed faktycznym uruchomieniem funkcji testowej. Posiada już właściwość `result` ze `state` i `startTime`.
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Wywoływana po ustawieniu wyniku i stanu zadania.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Wywoływana zaraz po uruchomieniu funkcji testowej. Nie posiada jeszcze nowego stanu i 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. Nie posiada jeszcze właściwości `result`.
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Wywoływana po uruchomieniu pojedynczego zestawu. Posiada stan i wynik.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Jeśli zdefiniowana, zostanie wywołana zamiast standardowego podziału i obsługi zestawu Vitest.
* Hooki `before` i `after` nie zostaną zignorowane.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Jeśli zdefiniowana, zostanie wywołana zamiast standardowej obsługi Vitest. Przydatne, jeśli masz własną funkcję testową.
* Hooki `before` i `after` nie zostaną zignorowane.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Wywoływana, gdy zadanie jest aktualizowane. Działa tak samo jak `onTaskUpdate` w raporcie, ale jest uruchamiana w tym samym wątku co testy.
*/
onTaskUpdate?: (
task: [string, TaskResult | undefined, TaskMeta | 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 w module uruchamiającym, rozważ użycie `beforeAll` w `setupFiles` zamiast tego.
*/
extendTaskContext?: (context: TestContext) => TestContext;
/**
* Wywoływana, gdy importowane są określone pliki. Może być wywołana w dwóch sytuacjach: do zbierania testów i do importowania plików konfiguracyjnych.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Funkcja wywoływana, gdy moduł uruchamiający próbuje pobrać wartość, gdy `test.extend` jest używany z `{ injected: true }`
*/
injectValue?: (key: string) => unknown;
/**
* Konfiguracja dostępna publicznie.
*/
config: VitestRunnerConfig;
/**
* Nazwa aktualnej puli. Może wpływać na sposób interpretacji śladu stosu po stronie serwera.
*/
pool?: string;
}
Podczas inicjalizacji tej klasy, Vitest przekazuje konfigurację Vitest - powinieneś udostępnić ją jako właściwość config
:
import type { RunnerTestFile } from 'vitest';
import type { VitestRunner, VitestRunnerConfig } from 'vitest/suite';
import { VitestTestRunner } from 'vitest/runners';
class CustomRunner extends VitestTestRunner implements VitestRunner {
public config: VitestRunnerConfig;
constructor(config: VitestRunnerConfig) {
this.config = config;
}
onAfterRunFiles(files: RunnerTestFile[]) {
console.log('finished running', files);
}
}
export default CustomRunner;
WARNING
Vitest dostarcza 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 jest używana do importowania plików testowych w środowisku przyjaznym dla Vite. Oznacza to, że będzie ona rozwiązywać importy i przekształcać zawartość pliku w czasie rzeczywistym, tak aby Node.js mógł ją zrozumieć:
export default class Runner {
async importFile(filepath: string) {
await this.__vitest_executor.executeId(filepath);
}
}
WARNING
Jeśli nie używasz własnego runnera lub nie zdefiniowałeś metody runTask
, Vitest spróbuje automatycznie pobrać zadanie. Jeśli nie dodałeś funkcji za pomocą setFn
, to się nie powiedzie.
TIP
Funkcjonalność snapshotów i niektóre inne funkcje zależą od modułu uruchamiającego. Jeśli nie chcesz ich stracić, możesz rozszerzyć swój moduł uruchamiający z VitestTestRunner
importowanego z vitest/runners
. Udostępnia on również BenchmarkNodeRunner
, jeśli chcesz rozszerzyć funkcjonalność benchmarków.
Zadania
WARNING
"Runner Tasks API" jest eksperymentalne i powinno być używane przede wszystkim w środowisku uruchomieniowym testów. Vitest udostępnia również "Reported Tasks API", które powinno być preferowane podczas pracy w głównym wątku (na przykład w raporcie).
Zespół aktualnie dyskutuje, czy "Runner Tasks" powinny zostać zastąpione "Reported Tasks" w przyszłości.
Zestawy i testy są wewnętrznie nazywane tasks
. Moduł uruchamiający Vitest inicjuje zadanie File
przed zebraniem jakichkolwiek testów - jest to nadzbiór Suite
z kilkoma dodatkowymi właściwościami. Jest ono dostępne dla każdego zadania (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 testowego, 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;
}
Każdy zestaw ma właściwość tasks
, która jest wypełniana podczas fazy kolekcji. Jest to przydatne do przechodzenia przez drzewo zadań od góry do dołu.
interface Suite extends TaskBase {
type: 'suite';
/**
* Zadanie związane z plikiem. Jest to zadanie główne pliku.
*/
file: File;
/**
* Lista 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 przechodzenia przez zadania od dołu do góry.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Kontekst testu, który zostanie przekazany do funkcji testowej.
*/
context: TestContext & ExtraContext;
/**
* Zadanie związane z plikiem. Jest to zadanie główne pliku.
*/
file: File;
/**
* Czy zadanie jest pominięte przez wywołanie `context.skip()`.
*/
pending?: boolean;
/**
* Czy zadanie ma zostać uznane za pomyślne, nawet jeśli zakończy się niepowodzeniem. Jeśli zadanie się nie powiedzie, zostanie oznaczone jako zaliczone.
*/
fails?: boolean;
/**
* Przechowuje promisy (z asynchronicznych wywołań expect), aby poczekać na nie przed zakończeniem testu
*/
promises?: Promise<any>[];
}
Każde zadanie może mieć pole result
. Zestawy mogą mieć to pole tylko wtedy, gdy błąd zgłoszony w wywołaniu zwrotnym zestawu lub wywołaniach zwrotnych 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 obecny w task.result.errors
.
export interface TaskResult {
/**
* Stan zadania. Przejmuje `task.mode` podczas zbierania.
* Gdy zadanie zostanie zakończone, zmieni się na `pass` lub `fail`.
* - **pass**: zadanie zakończyło się pomyślnie
* - **fail**: zadanie nie powiodło się
*/
state: TaskState;
/**
* Błędy, które wystąpiły podczas wykonywania zadania. Może wystąpić kilka błędów,
* jeśli `expect.soft()` nie powiodło się wielokrotnie.
*/
errors?: ErrorWithDiff[];
/**
* Ile milisekund zajęło wykonanie zadania.
*/
duration?: number;
/**
* Czas w milisekundach, kiedy zadanie rozpoczęło się.
*/
startTime?: number;
/**
* Rozmiar sterty w bajtach po zakończeniu zadania.
* Dostępne tylko, jeśli opcja `logHeapUsage` jest ustawiona i `process.memoryUsage` jest zdefiniowane.
*/
heap?: number;
/**
* Stan hooków związanych z tym zadaniem. Przydatne podczas raportowania.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* Liczba ponowień zadania. Zadanie jest ponawiane tylko, jeśli
* nie powiodło się i opcja `retry` jest ustawiona.
*/
retryCount?: number;
/**
* Liczba powtórzeń zadania. Zadanie jest powtarzane tylko, jeśli
* opcja `repeats` jest ustawiona. Ta liczba zawiera również `retryCount`.
*/
repeatCount?: number;
}
Twoja funkcja zadania
Vitest udostępnia narzędzie createTaskCollector
do tworzenia własnej metody test
. Zachowuje się ona tak samo jak test, ale wywołuje niestandardową metodę podczas zbierania.
Zadanie to obiekt, który jest częścią zestawu. Jest on automatycznie dodawany do bieżącego zestawu za pomocą metody suite.task
:
import { createTaskCollector, getCurrentSuite } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// Ta funkcja zostanie wywołana podczas fazy kolekcji:
// nie wywołuj tutaj funkcji obsługującej, dodaj ją do zadań zestawu
// za pomocą metody "getCurrentSuite().task()"
// Uwaga: createTaskCollector zapewnia wsparcie dla "todo"/"each"/...
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // aby "todo"/"skip"/... było śledzone poprawnie
meta: {
customPropertyToDifferentiateTask: true,
},
handler: fn,
timeout,
});
});
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