Runner API
WARNING
これは高度なAPIです。単にテストを実行するだけであれば、おそらく必要ありません。主にライブラリ開発者向けのAPIです。
設定ファイルでrunner
オプションを使用して、テストランナーのパスを指定できます。このファイルは、以下のメソッドを実装するクラスのコンストラクタをデフォルトエクスポートする必要があります。
export interface VitestRunner {
/**
* テストの収集と実行を開始する前に最初に呼び出されるメソッドです。
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* テストを収集した後、"onBeforeRun"の前に呼び出されます。
*/
onCollected?: (files: File[]) => unknown;
/**
* テストランナーが次のテスト実行をキャンセルする必要があるときに呼び出されます。
* ランナーはこのメソッドをリッスンし、呼び出された際には"onBeforeRunSuite"と"onBeforeRunTask"でテストとスイートをスキップとしてマークする必要があります。
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* 個々のテストを実行する前に呼び出されます。この時点では"result"はまだ含まれていません。
*/
onBeforeRunTask?: (test: TaskPopulated) => unknown;
/**
* 実際にテスト関数を実行する前に呼び出されるメソッドです。この時点では"state"と"startTime"を含む"result"が設定されています。
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 結果と状態が設定された後に呼び出されます。
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* テスト関数を実行した直後に呼び出されます。まだ新しい状態は設定されていません。テスト関数が例外をスローした場合には呼び出されません。
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 個々のスイートを実行する前に呼び出されます。この時点では"result"はまだ含まれていません。
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* 個々のスイートを実行した後に呼び出されます。状態と結果が設定されています。
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* 定義されている場合、通常のVitestのスイート分割と処理の代わりに呼び出されます。
* "before"および"after"フックは無視されません。
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* 定義されている場合、通常のVitestの処理の代わりに呼び出されます。カスタムテスト関数を使用している場合に便利です。
* "before"および"after"フックは無視されません。
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* タスクが更新されたときに呼び出されるメソッドです。レポーター用の"onTaskUpdate"と同じですが、これはテストと同じスレッドで実行されます。
*/
onTaskUpdate?: (
task: [string, TaskResult | undefined, TaskMeta | undefined][]
) => Promise<void>;
/**
* 収集されたパス内のすべてのテストを実行する前に呼び出されるメソッドです。
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* 収集されたパス内のすべてのテストを実行した直後に呼び出されるメソッドです。
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* テストの新しいコンテキストが定義されたときに呼び出されます。カスタムプロパティをコンテキストに追加したい場合に便利です。
* ランナーでカスタムコンテキストを定義したいだけであれば、代わりに"setupFiles"で"beforeAll"を使用することを検討してください。
*/
extendTaskContext?: (context: TestContext) => TestContext;
/**
* 特定のファイルがインポートされたときに呼び出されるメソッドです。テストを収集する場合とセットアップファイルをインポートする場合の2つの状況で呼び出される可能性があります。
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* `test.extend`が`{ injected: true }`とともに使用され、ランナーが値を取得しようとするときに呼び出される関数です。
*/
injectValue?: (key: string) => unknown;
/**
* 公開されている設定です。
*/
config: VitestRunnerConfig;
/**
* 現在のプールの名前。サーバー側でスタックトレースが推論される方法に影響を与える可能性があります。
*/
pool?: string;
}
このクラスを初期化する際、Vitestは設定を渡します。この設定は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はViteNodeRunner
のインスタンスを__vitest_executor
プロパティとして注入します。importFile
メソッドでファイルを処理するためにこれを使用できます(これはTestRunner
とBenchmarkRunner
のデフォルトの動作です)。
ViteNodeRunner
はexecuteId
メソッドを公開しており、これはVite互換環境でテストファイルをインポートするために使用されます。つまり、実行時にインポートを解決し、ファイル内容を変換することでNodeが理解できるようにします。
export default class Runner {
async importFile(filepath: string) {
await this.__vitest_executor.executeId(filepath);
}
}
WARNING
カスタムランナーがない場合、またはrunTask
メソッドを定義していない場合、Vitestは自動的にタスクを取得しようとします。setFn
で関数を追加していない場合、失敗します。
TIP
スナップショットサポートやその他の機能はランナーに依存します。これらの機能を失いたくない場合は、vitest/runners
からインポートされるVitestTestRunner
を継承してランナーを拡張できます。ベンチマーク機能を拡張したい場合は、BenchmarkNodeRunner
も公開されています。
タスク
WARNING
「Runner Tasks API」は実験的であり、主にテストランタイムでのみ使用されるべきです。Vitestは「Reported Tasks API」も公開しており、メインスレッド(例えばレポーター内)で作業する場合はこちらを優先すべきです。
開発チームは現在、「Runner Tasks」を将来的に「Reported Tasks」に置き換えるべきかどうかを検討中です。
スイートとテストは内部的にtasks
と呼ばれます。Vitestランナーは、テストを収集する前にFile
タスクを初期化します。これはいくつかの追加プロパティを持つSuite
のスーパーセットです。これはすべてのタスク(File
を含む)でfile
プロパティとして利用できます。
interface File extends Suite {
/**
* ファイルが属するプールの名前です。
* @default 'forks'
*/
pool?: string;
/**
* UNIX形式のファイルへのパスです。
*/
filepath: string;
/**
* ファイルが属するテストプロジェクトの名前です。
*/
projectName: string | undefined;
/**
* ファイル内のすべてのテストを収集するのにかかった時間。
* この時間には、すべてのファイル依存関係のインポートも含まれます。
*/
collectDuration?: number;
/**
* セットアップファイルをインポートするのにかかった時間です。
*/
setupDuration?: number;
}
すべてのスイートには、収集フェーズ中に設定されるtasks
プロパティがあります。タスクツリーをトップダウンで走査する際に便利です。
interface Suite extends TaskBase {
type: 'suite';
/**
* ファイルタスクです。ファイルのルートタスクとなります。
*/
file: File;
/**
* スイートの一部であるタスクの配列です。
*/
tasks: Task[];
}
すべてのタスクには、それが配置されているスイートを参照するsuite
プロパティがあります。test
またはdescribe
がトップレベルで初期化された場合、それらにはsuite
プロパティがありません(file
とは異なります)。File
もsuite
プロパティを持つことはありません。タスクを下から上にたどるのに便利です。
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* テスト関数に渡されるテストコンテキストです。
*/
context: TestContext & ExtraContext;
/**
* ファイルタスクです。ファイルのルートタスクとなります。
*/
file: File;
/**
* `context.skip()`を呼び出すことによってタスクがスキップされたかどうかを示します。
*/
pending?: boolean;
/**
* タスクが失敗した場合でも成功とみなすべきかどうか。タスクが失敗した場合、パスとしてマークされます。
*/
fails?: boolean;
/**
* (非同期expectからの)Promiseを保持し、テストを終了する前にそれらを待ちます。
*/
promises?: Promise<any>[];
}
すべてのタスクにはresult
フィールドを持つことができます。スイートは、スイートコールバックまたはbeforeAll
/afterAll
コールバック内でエラーがスローされ、テストの収集が妨げられた場合にのみこのフィールドを持つことができます。テストは、コールバックが呼び出された後、常にこのフィールドを持ちます。state
とerrors
フィールドは結果に応じて存在します。beforeEach
またはafterEach
コールバックでエラーがスローされた場合、スローされたエラーはtask.result.errors
に存在します。
export interface TaskResult {
/**
* タスクの状態です。収集中に`task.mode`を継承します。
* タスクが完了すると、`pass`または`fail`に変更されます。
* - **pass**: タスクが正常に実行された
* - **fail**: タスクが失敗した
*/
state: TaskState;
/**
* タスク実行中に発生したエラーです。`expect.soft()`が複数回失敗した場合、複数のエラーが発生する可能性があります。
*/
errors?: ErrorWithDiff[];
/**
* タスクの実行にかかった時間(ミリ秒)です。
*/
duration?: number;
/**
* タスクの実行が開始された時間(ミリ秒)です。
*/
startTime?: number;
/**
* タスク完了後のヒープ使用量(バイト単位)です。
* `logHeapUsage`オプションが設定されており、`process.memoryUsage`が定義されている場合にのみ利用可能です。
*/
heap?: number;
/**
* このタスクに関連するフックの状態です。レポート時に便利です。
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* タスクがリトライされた回数です。タスクは失敗し、`retry`オプションが設定されている場合にのみリトライされます。
*/
retryCount?: number;
/**
* タスクが繰り返された回数です。タスクは`repeats`オプションが設定されている場合にのみ繰り返されます。この数値には`retryCount`も含まれます。
*/
repeatCount?: number;
}
あなたのタスク関数
Vitestは、独自のtest
メソッドを作成するためのcreateTaskCollector
ユーティリティを公開しています。これはテストと同じように動作しますが、収集中にカスタムメソッドを呼び出します。
タスクはスイートの一部であるオブジェクトです。suite.task
メソッドを使用して現在のスイートに自動的に追加されます。
import { createTaskCollector, getCurrentSuite } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// この関数は収集フェーズ中に呼び出されます。
// ここで関数ハンドラを呼び出さず、"getCurrentSuite().task()"メソッドで
// スイートタスクに追加してください。
// 注: createTaskCollectorは"todo"/"each"/...のサポートを提供します。
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // "todo"/"skip"/...が正しく追跡されるようにします。
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