レポーターの拡張方法
vitest/reporters
からレポーターをインポートし、それらを拡張することでカスタムレポーターを作成できます。
組み込みレポーターの拡張
一般的に、レポーターを一から作成する必要はありません。vitest
には、拡張可能なデフォルトのレポーターがいくつか付属しています。
import { DefaultReporter } from 'vitest/reporters';
export default class MyDefaultReporter extends DefaultReporter {
// 処理を追加
}
もちろん、レポーターをゼロから作成することも可能です。BaseReporter
クラスを拡張し、必要なメソッドを実装するだけです。
以下にカスタムレポーターの例を示します。
// ./custom-reporter.js
import { BaseReporter } from 'vitest/reporters';
export default class CustomReporter extends BaseReporter {
onCollected() {
const files = this.ctx.state.getFiles(this.watchFilters);
this.reportTestSummary(files);
}
}
または、Reporter
インターフェースを実装します。
// ./custom-reporter.js
import { Reporter } from 'vitest/reporters';
export default class CustomReporter implements Reporter {
onCollected() {
// 結果を出力
}
}
次に、vitest.config.ts
ファイルでカスタムレポーターを設定できます。
import { defineConfig } from 'vitest/config';
import CustomReporter from './custom-reporter.js';
export default defineConfig({
test: {
reporters: [new CustomReporter()],
},
});
レポート対象のタスク
WARNING
これは実験的な API です。破壊的な変更が SemVer に従わない可能性があります。使用する際は Vitest のバージョンを固定してください。
この API へのアクセスは、vitest.state.getReportedEntity(runnerTask)
を呼び出すことで行います。
import type { Vitest } from 'vitest/node';
import type { RunnerTestFile } from 'vitest';
import type { Reporter, TestModule } from 'vitest/reporters';
class MyReporter implements Reporter {
ctx!: Vitest;
onInit(ctx: Vitest) {
this.ctx = ctx;
}
onFinished(files: RunnerTestFile[]) {
for (const fileTask of files) {
// 古いタスクの実装では "module" の代わりに "file" を使用していることに注意してください。
const testModule = this.ctx.state.getReportedEntity(
fileTask
) as TestModule;
for (const task of testModule.children) {
// ^?
console.log('finished', task.type, task.fullName);
}
}
}
}
Vitest 2.1 でこの API を安定させる予定です。
TestCase
TestCase
は単一のテストを表します。
declare class TestCase {
readonly type = 'test' | 'custom';
/**
* タスクのインスタンス。
* @experimental 公開タスク API は実験的であり、SemVer に従いません。
*/
readonly task: RunnerTestCase | RunnerCustomCase;
/**
* テストに関連付けられたプロジェクト。
*/
readonly project: TestProject;
/**
* テストが定義されているテストモジュールへの直接参照。
*/
readonly module: TestModule;
/**
* テストの名前。
*/
readonly name: string;
/**
* すべての親スイートを `>` で区切ったテストのフルネーム。
*/
readonly fullName: string;
/**
* 一意の識別子。
* この ID は決定的であり、複数回の実行で同じテストに対して同じになります。
* ID はプロジェクト名、モジュール ID、テストの位置に基づいています。
*/
readonly id: string;
/**
* テストが定義されたモジュール内の位置。
* 位置は、設定で `includeTaskLocation` が有効になっている場合にのみ収集されます。
*/
readonly location: { line: number; column: number } | undefined;
/**
* 親スイート。テストがモジュール内で直接呼び出された場合、親はモジュール自体になります。
*/
readonly parent: TestSuite | TestModule;
/**
* テストが開始されたオプション。
*/
readonly options: TaskOptions;
/**
* テストがスイートを失敗させていないかどうかを確認します。
* テストがまだ終了していないかスキップされた場合は、`true` を返します。
*/
ok(): boolean;
/**
* テスト実行時にアタッチされるカスタムメタデータ。
*/
meta(): TaskMeta;
/**
* テスト結果。テストがまだ終了していないか収集されたばかりの場合は `undefined` になります。
*/
result(): TestResult | undefined;
/**
* テストに関する有用な情報(実行時間、メモリ使用量など)。
*/
diagnostic(): TestDiagnostic | undefined;
}
export type TestResult =
| TestResultPassed
| TestResultFailed
| TestResultSkipped;
export interface TestResultPassed {
/**
* テストは正常に合格しました。
*/
state: 'passed';
/**
* テスト実行中にスローされたエラー。
*
* **注**: テストが正常に再試行された場合でも、エラーは報告されます。
*/
errors: TestError[] | undefined;
}
export interface TestResultFailed {
/**
* テストの実行に失敗しました。
*/
state: 'failed';
/**
* テスト実行中にスローされたエラー。
*/
errors: TestError[];
}
export interface TestResultSkipped {
/**
* テストは `only`、`skip`、または `todo` フラグでスキップされました。
* 使用されたフラグは `mode` オプションで確認できます。
*/
state: 'skipped';
/**
* スキップされたテストにはエラーがありません。
*/
errors: undefined;
}
export interface TestDiagnostic {
/**
* テストの実行時間が `slowTestThreshold` を超えている場合。
*/
slow: boolean;
/**
* テストが使用したメモリ量(バイト単位)。
* この値は、テストが `logHeapUsage` フラグで実行された場合にのみ利用可能です。
*/
heap: number | undefined;
/**
* テストの実行時間(ミリ秒単位)。
*/
duration: number;
/**
* テストが開始された時間(ミリ秒単位)。
*/
startTime: number;
/**
* テストが再試行された回数。
*/
retryCount: number;
/**
* `repeats` オプションで設定された回数だけテストが繰り返された回数。
* この値は、繰り返し中にテストが失敗し、`retry` が設定されていない場合は低くなる可能性があります。
*/
repeatCount: number;
/**
* 2回目の再試行でテストが合格した場合。
*/
flaky: boolean;
}
TestSuite
TestSuite
は、テストと他のスイートを含む単一のスイートを表します。
declare class TestSuite {
readonly type = 'suite';
/**
* タスクのインスタンス。
* @experimental 公開タスク API は実験的であり、SemVer に従いません。
*/
readonly task: RunnerTestSuite;
/**
* テストに関連付けられたプロジェクト。
*/
readonly project: TestProject;
/**
* スイートが定義されているテストモジュールへの直接参照。
*/
readonly module: TestModule;
/**
* スイートの名前。
*/
readonly name: string;
/**
* すべての親スイートを `>` で区切ったスイートのフルネーム。
*/
readonly fullName: string;
/**
* 一意の識別子。
* この ID は決定的であり、複数回の実行で同じテストに対して同じになります。
* ID はプロジェクト名、モジュール ID、テストの位置に基づいています。
*/
readonly id: string;
/**
* スイートが定義されたモジュール内の位置。
* 位置は、設定で `includeTaskLocation` が有効になっている場合にのみ収集されます。
*/
readonly location: { line: number; column: number } | undefined;
/**
* このスイートの一部であるスイートとテストのコレクション。
*/
readonly children: TaskCollection;
/**
* スイートが開始されたオプション。
*/
readonly options: TaskOptions;
}
TestModule
TestModule
は、スイートとテストを含む単一のファイルを表します。
declare class TestModule extends SuiteImplementation {
readonly type = 'module';
/**
* タスクのインスタンス。
* @experimental 公開タスク API は実験的であり、SemVer に従いません。
*/
readonly task: RunnerTestFile;
/**
* このモジュールの一部であるスイートとテストのコレクション。
*/
readonly children: TestCollection;
/**
* これは通常、絶対 Unix ファイルパスです。
* ファイルがディスク上にない場合は仮想 ID になることがあります。
* この値は Vite の `ModuleGraph` ID に対応します。
*/
readonly moduleId: string;
/**
* モジュールに関する有用な情報(実行時間、メモリ使用量など)。
* モジュールがまだ実行されていない場合、すべての診断値は `0` を返します。
*/
diagnostic(): ModuleDiagnostic;
}
export interface ModuleDiagnostic {
/**
* 環境のインポートと初期化にかかる時間。
*/
environmentSetupDuration: number;
/**
* Vitest がテストハーネス(ランナー、モックなど)をセットアップするのにかかる時間。
*/
prepareDuration: number;
/**
* テストモジュールのインポートにかかる時間。
* これには、モジュール内のすべてのインポートとスイートコールバックの実行が含まれます。
*/
collectDuration: number;
/**
* セットアップモジュールのインポートにかかる時間。
*/
setupDuration: number;
/**
* モジュール内のすべてのテストとフックの累積実行時間。
*/
duration: number;
}
TestCollection
TestCollection
は、スイートとテストのコレクションを表します。また、自身を反復処理するための便利なメソッドも提供します。
declare class TestCollection {
/**
* 配列内の特定のインデックスにあるテストまたはスイートを返します。
*/
at(index: number): TestCase | TestSuite | undefined;
/**
* コレクション内のテストとスイートの数。
*/
size: number;
/**
* 操作しやすい配列形式でコレクションを返します。
*/
array(): (TestCase | TestSuite)[];
/**
* このコレクションとその子の一部であるすべてのスイートをフィルタリングします。
*/
allSuites(): IterableIterator<TestSuite>;
/**
* このコレクションとその子の一部であるすべてのテストをフィルタリングします。
*/
allTests(state?: TestResult['state'] | 'running'): IterableIterator<TestCase>;
/**
* このコレクションの一部であるテストのみをフィルタリングします。
*/
tests(state?: TestResult['state'] | 'running'): IterableIterator<TestCase>;
/**
* このコレクションの一部であるスイートのみをフィルタリングします。
*/
suites(): IterableIterator<TestSuite>;
[Symbol.iterator](): IterableIterator<TestSuite | TestCase>;
}
たとえば、testModule.children.allTests()
を呼び出すことで、モジュール内のすべてのテストを反復処理できます。
function onFileCollected(testModule: TestModule): void {
console.log('collecting tests in', testModule.moduleId);
// モジュール内のすべてのテストとスイートを反復処理します
for (const task of testModule.children.allTests()) {
console.log('collected', task.type, task.fullName);
}
}
TestProject
TestProject
は、モジュールに関連付けられたプロジェクトです。そのモジュール内のすべてのテストとスイートは、同じプロジェクトを参照します。
プロジェクトを使用して設定や提供されたコンテキストを取得できます。
declare class TestProject {
/**
* グローバルな Vitest インスタンス。
* @experimental 公開 Vitest API は実験的であり、SemVer に従いません。
*/
readonly vitest: Vitest;
/**
* このテストプロジェクトが関連付けられているワークスペースプロジェクト。
* @experimental 公開 Vitest API は実験的であり、SemVer に従いません。
*/
readonly workspaceProject: WorkspaceProject;
/**
* Vite の開発サーバーインスタンス。各ワークスペースプロジェクトは独自のサーバーを持っています。
*/
readonly vite: ViteDevServer;
/**
* 解決されたプロジェクト設定。
*/
readonly config: ResolvedProjectConfig;
/**
* 解決されたグローバル設定。ワークスペースプロジェクトがない場合、これは `config` と同じになります。
*/
readonly globalConfig: ResolvedConfig;
/**
* シリアライズ済みのプロジェクト設定。これはテストが受け取る設定です。
*/
get serializedConfig(): SerializedConfig;
/**
* プロジェクトの名前。設定されていない場合は空文字列。
*/
name(): string;
/**
* プロジェクトに提供されたカスタムコンテキスト。
*/
context(): ProvidedContext;
/**
* プロジェクトにカスタムのシリアライズ可能なコンテキストを提供します。このコンテキストは、テスト実行時に利用可能になります。
*/
provide<T extends keyof ProvidedContext & string>(
key: T,
value: ProvidedContext[T]
): void;
}
エクスポートされているレポーター
vitest
には、すぐに使用できるいくつかの組み込みレポーターが付属しています。
組み込みレポーター:
BasicReporter
DefaultReporter
DotReporter
JsonReporter
VerboseReporter
TapReporter
JUnitReporter
TapFlatReporter
HangingProcessReporter
基底抽象レポーター:
BaseReporter
インターフェースレポーター:
Reporter