扩展报告器
你可以从 vitest/reporters
导入报告器,并通过扩展它们来创建自定义报告器。
扩展内置报告器
通常,你不需要从头开始创建报告器。vitest
默认提供了一些报告器,你可以扩展它们。
ts
import { DefaultReporter } from 'vitest/reporters';
export default class MyDefaultReporter extends DefaultReporter {
// 执行某些操作
}
当然,你也可以从零开始创建报告器。只需扩展 BaseReporter
类并实现所需的方法。
下面是一个自定义报告器的示例:
ts
// ./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
接口:
ts
// ./custom-reporter.js
import { Reporter } from 'vitest/reporters';
export default class CustomReporter implements Reporter {
onCollected() {
// 输出内容
}
}
然后你可以在 vitest.config.ts
文件中使用你的自定义报告器:
ts
import { defineConfig } from 'vitest/config';
import CustomReporter from './custom-reporter.js';
export default defineConfig({
test: {
reporters: [new CustomReporter()],
},
});
已报告的任务
WARNING
这是一个实验性 API。可能会有不遵循 SemVer 的重大更改。使用时请固定 Vitest 的版本。
你可以通过调用 vitest.state.getReportedEntity(runnerTask)
来访问此 API:
ts
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) {
// 请注意,旧的任务实现使用的是 'file' 而非 'module'
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
代表一个单独的测试用例。
ts
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;
/**
* 如果测试在第二次重试时通过。
*/
flaky: boolean;
}
TestSuite
TestSuite
代表一个包含测试和其他套件的测试套件。
ts
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
代表一个包含套件和测试的单独文件(测试模块)。
ts
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 设置测试工具(runner、mocks 等)所需的时间。
*/
prepareDuration: number;
/**
* 导入测试模块所需的时间。
* 这包括导入模块中的所有内容并执行套件回调。
*/
collectDuration: number;
/**
* 导入 setup 模块所需的时间。
*/
setupDuration: number;
/**
* 模块中所有测试和钩子的累积持续时间。
*/
duration: number;
}
TestCollection
TestCollection
代表套件和测试的集合。它还提供了一些有用的方法来遍历集合中的元素。
ts
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()
来遍历模块内的所有测试:
ts
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
是与模块关联的项目。该模块内的每个测试和套件都将引用同一个项目。
通过项目,可以方便地获取配置信息或访问提供的上下文。
ts
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