Runner API
WARNING
Dies ist eine fortgeschrittene API. Wenn Sie lediglich Tests ausführen möchten, benötigen Sie diese wahrscheinlich nicht. Sie wird hauptsächlich von Bibliotheksautoren verwendet.
Sie können den Pfad zu Ihrem Test-Runner mit der Option runner
in Ihrer Konfigurationsdatei angeben. Diese Datei sollte einen Standardexport mit einem Klassenkonstruktor enthalten, der die folgende Schnittstelle implementiert:
export interface VitestRunner {
/**
* Wird als Erstes aufgerufen, bevor Tests tatsächlich gesammelt und ausgeführt werden.
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* Wird nach dem Sammeln von Tests und vor "onBeforeRun" aufgerufen.
*/
onCollected?: (files: File[]) => unknown;
/**
* Wird aufgerufen, wenn der Test-Runner die nächsten Testläufe abbrechen soll.
* Der Runner sollte diese Methode überwachen und Tests sowie Suiten in
* "onBeforeRunSuite" und "onBeforeRunTask" als übersprungen markieren, sobald sie aufgerufen wird.
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* Wird vor dem Ausführen eines einzelnen Tests aufgerufen. Enthält noch kein 'result'.
*/
onBeforeRunTask?: (test: TaskPopulated) => unknown;
/**
* Wird vor dem eigentlichen Ausführen der Testfunktion aufgerufen. Verfügt bereits über 'result' mit 'state' und 'startTime'.
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Wird aufgerufen, nachdem das Ergebnis und der Status festgelegt wurden.
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* Wird direkt nach dem Ausführen der Testfunktion aufgerufen. Der neue Status steht noch nicht fest. Wird nicht aufgerufen, falls die Testfunktion einen Fehler auslöst.
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* Wird vor dem Ausführen einer einzelnen Suite aufgerufen. Enthält noch kein 'result'.
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* Wird nach dem Ausführen einer einzelnen Suite aufgerufen. Hat Status und Ergebnis.
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* Wenn definiert, wird diese Methode anstelle der üblichen Vitest-Suite-Partitionierung und -Behandlung aufgerufen.
* "before"- und "after"-Hooks werden nicht ignoriert.
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* Wenn definiert, wird diese Methode anstelle der üblichen Vitest-Behandlung aufgerufen. Dies ist nützlich, wenn Sie Ihre eigene Testfunktion haben.
* "before"- und "after"-Hooks werden nicht ignoriert.
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* Wird aufgerufen, wenn eine Aufgabe aktualisiert wird. Dasselbe wie "onTaskUpdate" in einem Reporter, jedoch läuft dies im selben Thread wie die Tests.
*/
onTaskUpdate?: (
task: [string, TaskResult | undefined, TaskMeta | undefined][]
) => Promise<void>;
/**
* Wird vor dem Ausführen aller Tests in gesammelten Pfaden aufgerufen.
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* Wird direkt nach dem Ausführen aller Tests in gesammelten Pfaden aufgerufen.
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* Wird aufgerufen, wenn ein neuer Kontext für einen Test definiert wird. Dies ist nützlich, wenn Sie dem Kontext benutzerdefinierte Eigenschaften hinzufügen möchten.
* Wenn Sie lediglich einen benutzerdefinierten Kontext mit einem Runner definieren möchten, sollten Sie stattdessen "beforeAll" in "setupFiles" verwenden.
*/
extendTaskContext?: (context: TestContext) => TestContext;
/**
* Wird aufgerufen, wenn bestimmte Dateien importiert werden. Kann in zwei Situationen aufgerufen werden: zum Sammeln von Tests und zum Importieren von Setup-Dateien.
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* Funktion, die aufgerufen wird, wenn der Runner versucht, den Wert zu erhalten, sobald `test.extend` mit `{ injected: true }` verwendet wird.
*/
injectValue?: (key: string) => unknown;
/**
* Öffentlich zugängliche Konfiguration.
*/
config: VitestRunnerConfig;
/**
* Der Name des aktuellen Pools. Beeinflusst möglicherweise, wie der Stack-Trace auf der Serverseite ermittelt wird.
*/
pool?: string;
}
Beim Instanziieren dieser Klasse übergibt Vitest die Vitest-Konfiguration, die Sie als config
-Eigenschaft verfügbar machen sollten:
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 injiziert auch eine Instanz von ViteNodeRunner
als Eigenschaft __vitest_executor
. Sie können diese verwenden, um Dateien in der Methode importFile
zu verarbeiten (dies ist das Standardverhalten von TestRunner
und BenchmarkRunner
).
ViteNodeRunner
stellt die Methode executeId
zur Verfügung, die verwendet wird, um Testdateien in einer Vite-freundlichen Umgebung zu importieren. Das bedeutet, sie löst Importe auf und transformiert den Dateiinhalt zur Laufzeit, damit Node.js ihn verstehen kann:
export default class Runner {
async importFile(filepath: string) {
await this.__vitest_executor.executeId(filepath);
}
}
WARNING
Wenn Sie keinen benutzerdefinierten Runner haben oder die Methode runTask
nicht definiert haben, versucht Vitest, eine Aufgabe automatisch abzurufen. Wenn Sie keine Funktion mit setFn
hinzugefügt haben, wird dies fehlschlagen.
TIP
Snapshot-Unterstützung und einige andere Funktionen hängen vom Runner ab. Wenn Sie diese nicht verlieren möchten, können Sie Ihren Runner von VitestTestRunner
erweitern, das von vitest/runners
importiert wird. Es stellt auch BenchmarkNodeRunner
zur Verfügung, wenn Sie die Benchmark-Funktionalität erweitern möchten.
Aufgaben
WARNING
Die "Runner Tasks API" ist experimentell und sollte primär nur in der Testlaufzeit verwendet werden. Vitest stellt auch die "Reported Tasks API" zur Verfügung, die bevorzugt werden sollte, wenn im Hauptthread (z.B. innerhalb des Reporters) gearbeitet wird.
Das Team diskutiert derzeit, ob "Runner Tasks" zukünftig durch "Reported Tasks" ersetzt werden sollen.
Suiten und Tests werden intern als Aufgaben
bezeichnet. Der Vitest-Runner initialisiert eine File
-Aufgabe, bevor er Tests sammelt – dies ist eine erweiterte Version von Suite
mit einigen zusätzlichen Eigenschaften. Diese ist für jede Aufgabe (einschließlich File
) als file
-Eigenschaft verfügbar.
interface File extends Suite {
/**
* Der Name des Pools, zu dem die Datei gehört.
* @default 'forks'
*/
pool?: string;
/**
* Der Pfad zur Datei im UNIX-Format.
*/
filepath: string;
/**
* Der Name des Testprojekts, zu dem die Datei gehört.
*/
projectName: string | undefined;
/**
* Die Zeit, die zum Sammeln aller Tests in der Datei benötigt wurde.
* Diese Zeit beinhaltet auch das Importieren aller Dateiabhängigkeiten.
*/
collectDuration?: number;
/**
* Die Zeit, die zum Importieren der Setup-Datei benötigt wurde.
*/
setupDuration?: number;
}
Jede Suite hat eine tasks
-Eigenschaft, die während der Sammelphase gefüllt wird. Dies ist nützlich, um den Aufgabenbaum von oben nach unten zu traversieren.
interface Suite extends TaskBase {
type: 'suite';
/**
* Datei-Aufgabe. Es ist die Wurzelaufgabe der Datei.
*/
file: File;
/**
* Ein Array von Aufgaben, die Teil der Suite sind.
*/
tasks: Task[];
}
Jede Aufgabe hat eine suite
-Eigenschaft, die auf die Suite verweist, in der sie sich befindet. Wenn test
oder describe
auf der obersten Ebene initialisiert werden, besitzen sie keine suite
-Eigenschaft (sie wird nicht gleich file
sein!). File
hat auch niemals eine suite
-Eigenschaft. Dies ist nützlich, um die Aufgaben von unten nach oben zu traversieren.
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* Testkontext, der an die Testfunktion übergeben wird.
*/
context: TestContext & ExtraContext;
/**
* Datei-Aufgabe. Es ist die Wurzelaufgabe der Datei.
*/
file: File;
/**
* Ob die Aufgabe durch Aufruf von `context.skip()` übersprungen wurde.
*/
pending?: boolean;
/**
* Ob die Aufgabe erfolgreich sein sollte, wenn sie fehlschlägt. Wenn die Aufgabe fehlschlägt, wird sie als bestanden markiert.
*/
fails?: boolean;
/**
* Speichert Promises (von async expects), um auf sie zu warten, bevor der Test beendet wird.
*/
promises?: Promise<any>[];
}
Jede Aufgabe kann ein result
-Feld haben. Suiten können dieses Feld nur haben, wenn ein Fehler, der innerhalb eines Suite-Callbacks oder der beforeAll
-/afterAll
-Callbacks auftritt, sie daran hindert, Tests zu sammeln. Tests haben dieses Feld immer, nachdem ihre Rückruffunktionen aufgerufen wurden – die Felder state
und errors
sind je nach Ergebnis vorhanden. Wenn ein Fehler in beforeEach
oder afterEach
-Rückruffunktionen ausgelöst wurde, ist der entsprechende Fehler in task.result.errors
vorhanden.
export interface TaskResult {
/**
* Status der Aufgabe. Erbt den `task.mode` während der Sammlung.
* Nach Abschluss der Aufgabe wird er auf `pass` oder `fail` geändert.
* - **pass**: Aufgabe wurde erfolgreich durchgeführt
* - **fail**: Aufgabe fehlgeschlagen
*/
state: TaskState;
/**
* Fehler, die während der Aufgabenausführung aufgetreten sind. Es ist möglich, mehrere Fehler zu haben,
* wenn `expect.soft()` mehrfach fehlgeschlagen ist.
*/
errors?: ErrorWithDiff[];
/**
* Die Ausführungsdauer der Aufgabe in Millisekunden.
*/
duration?: number;
/**
* Zeitstempel in Millisekunden, zu dem die Aufgabe gestartet wurde.
*/
startTime?: number;
/**
* Speicherverbrauch in Bytes nach Beendigung der Aufgabe.
* Nur verfügbar, wenn die Option `logHeapUsage` gesetzt ist und `process.memoryUsage` definiert ist.
*/
heap?: number;
/**
* Status der mit dieser Aufgabe verbundenen Hooks. Für Berichtszwecke nützlich.
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* Die Anzahl der Wiederholungsversuche der Aufgabe. Die Aufgabe wird nur wiederholt, wenn sie
* fehlgeschlagen ist und die Option `retry` gesetzt ist.
*/
retryCount?: number;
/**
* Die Anzahl der Wiederholungen der Aufgabe. Die Aufgabe wird nur wiederholt, wenn
* die Option `repeats` gesetzt ist. Dieser Wert beinhaltet ebenfalls `retryCount`.
*/
repeatCount?: number;
}
Ihre Aufgabenfunktion
Vitest stellt die Hilfsfunktion createTaskCollector
zur Verfügung, um Ihre eigene test
-Methode zu erstellen. Sie verhält sich wie ein Test, ruft aber während der Sammlung eine benutzerdefinierte Methode auf.
Eine Aufgabe ist ein Objekt, das Teil einer Suite ist. Es wird automatisch der aktuellen Suite über die Methode suite.task
hinzugefügt:
import { createTaskCollector, getCurrentSuite } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// Diese Funktion wird während der Sammelphase aufgerufen:
// Rufen Sie hier keinen Funktionshandler auf, sondern fügen Sie ihn zu den Suite-Aufgaben hinzu
// mit der Methode "getCurrentSuite().task()"
// Hinweis: createTaskCollector unterstützt "todo"/"each"/...
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // damit "todo"/"skip"/... korrekt erfasst wird
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