For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/api/javascript-api/reporter.md.
close
  • 简体中文
  • Reporter API

    Reporter API 允许你创建自定义测试结果处理器和输出格式器。此接口是实验性的,在未来的版本中可能会发生变化。

    注意:使用示例和配置指南,请参阅 报告器指南

    使用自定义报告器

    通过实现 Reporter 接口来创建自定义报告器:

    import type { Reporter, TestFileResult } from '@rstest/core';
    
    const customReporter: Reporter = {
      onTestFileStart(test) {
        console.log(`Starting: ${test.testPath}`);
      },
    
      onTestFileResult(result) {
        console.log(`Finished: ${result.testPath}`);
      },
    
      onTestRunEnd({ results, duration }) {
        console.log(`All tests completed in ${duration.totalTime}ms`);
      },
    };

    在配置中使用自定义报告器:

    import { defineConfig } from '@rstest/core';
    import { customReporter } from './path/to/custom-reporter';
    
    export default defineConfig({
      reporters: [customReporter],
    });

    接口概览

    Reporter 接口为测试执行提供生命周期钩子。每个钩子在测试运行期间的具体时间点被调用。

    重要提示:有关最新的接口定义和完整类型签名,请参考 源代码

    钩子类别

    • 文件级钩子onTestFileStartonTestFileReadyonTestFileResult
    • 套件级钩子onTestSuiteStartonTestSuiteResult
    • 用例级钩子onTestCaseStartonTestCaseResult
    • 运行级钩子onTestRunStartonTestRunEndonUserConsoleLogonExit

    基本接口结构

    interface Reporter {
      onTestFileStart?(test: TestFileInfo): void;
      onTestFileReady?(test: TestFileInfo): void;
      onTestFileResult?(test: TestFileResult): void;
      onTestSuiteStart?(test: TestSuiteInfo): void;
      onTestSuiteResult?(result: TestResult): void;
      onTestCaseStart?(test: TestCaseInfo): void;
      onTestCaseResult?(result: TestResult): void;
      onTestRunEnd?(payload: {
        results: TestFileResult[]; // 测试文件结果;watch 模式下是整个会话的累计结果。
        testResults: TestResult[]; // 所有测试用例的结果,不按文件分组。
        summary: {
          tests: {
            total: number; // 测试总数。
            passed: number; // 通过的测试数量。
            failed: number; // 失败的测试数量。
            skipped: number; // 跳过的测试数量。
            todo: number; // todo 测试数量。
          };
          files: {
            total: number; // 测试文件总数。
            failed: number; // 失败的测试文件数量。
          };
        };
        duration: {
          totalTime: number; // 总时长(毫秒)。
          buildTime: number; // 构建时长(毫秒)。
          testTime: number; // 测试执行时长(毫秒)。
        };
        snapshotSummary: SnapshotSummary;
        unhandledErrors: {
          name?: string; // 错误类名称。
          message: string; // 错误信息。
          stack?: string; // 序列化后的调用栈。
          diff?: string; // 格式化后的断言差异。
          expected?: string; // 序列化后的断言预期值。
          actual?: string; // 序列化后的断言实际值。
          retryCount?: number; // 产生此错误的重试次数。
          fullStack?: boolean; // 是否打印完整调用栈。
        }[];
        coverage?: CoverageMapData; // 启用覆盖率时提供。
        rerunTestPaths?: string[]; // 仅 watch 模式:当前轮次执行的文件。
        getSourcemap: (sourcePath: string) => Promise<SourceMapInput | null>; // 获取构建产物的 source map。
      }): void | Promise<void>;
      onUserConsoleLog?(log: UserConsoleLog): void;
      onExit?(): MaybePromise<void>;
    }

    从 v0.12.0 开始,除了 CLI 异常退出,Rstest 还会在 reporter 所属 context 释放时调用 onExit,包括 run、list 或 merge 正常结束、watch close 和启动失败。list context 目前不会 attach reporter,因此没有可调用的 reporter Hook。可以用 onExit 释放 stream、renderer 等资源。Hook 抛出的错误不会覆盖操作本身的结果或错误。

    Reporter result 上可以读取 meta 字段。通过 context.task.meta 写入的可 JSON 序列化运行时 metadata 可以在 onTestCaseResult 收到的 TestResult 上读取。通过 hooks 的 ctx.meta 写入的 metadata 可以在 onTestSuiteResult 收到的对应 suite result 上读取,文件级 hook metadata 可以在 onTestFileResultTestFileResult.meta 上读取。

    onTestRunEnd

    每轮运行结束时调用 onTestRunEnd,参数里是这一轮结束时的结果和汇总。从 v0.12.0 开始,参数带上了 summaryrerunTestPathsunhandledErrors 固定是序列化后的错误数组。

    watch 模式下,resultstestResultssummary 是整个会话的累计结果:每个文件只保留最新一次的结果,删除的文件会移出。rerunTestPaths 是这一轮实际执行的文件,一次性运行没有这个字段。unhandledErrors 里的每一项都是普通对象,可以直接序列化为 JSON。

    TestResult

    TestResultTestFileResult@rstest/core 导出。套件和用例级钩子收到的是 TestResult,文件级钩子和 onTestRunEnd 收到的是 TestFileResult,它在 TestResult 的基础上多了这个文件里各个测试的结果。

    • 类型:
    type TestResultStatus = 'skip' | 'pass' | 'fail' | 'todo';
    
    interface TestResult {
      testId: string; // 任务标识。
      status: TestResultStatus; // 最终结果状态。
      name: string; // 测试、套件或测试文件的显示名称。
      testPath: string; // 所属测试文件的路径。
      parentNames?: string[]; // 外层测试套件的名称。
      duration?: number; // 执行时长(毫秒)。
      errors?: SerializedError[]; // 最后一次执行产生的错误。
      retryErrors?: SerializedError[]; // 之前重试产生的错误。
      retryCount?: number; // 已执行的重试次数。
      project: string; // 项目名称。
      meta?: TaskMeta; // 关联到任务的可序列化元数据。
      heap?: number; // 堆内存用量(字节),开启 `logHeapUsage` 时采样。
    }

    TestFileResult

    • 类型:
    interface TestFileResult extends TestResult {
      results: TestResult[]; // 当前文件中声明的测试结果。
      snapshotResult?: SnapshotResult; // 当前文件的快照统计。
      coverage?: Record<string, FileCoverageData>; // 开启覆盖率时该文件的覆盖率数据。
    }

    SerializedError 的结构和 onTestRunEnd 参数里 unhandledErrors 的每一项相同。SnapshotResult 来自 @vitest/snapshotFileCoverageData 来自 istanbul-lib-coverage

    示例

    简单自定义报告器

    import type { Reporter } from '@rstest/core';
    
    const simpleReporter: Reporter = {
      onTestFileStart(test) {
        console.log(`📁 ${test.testPath}`);
      },
    
      onTestCaseResult(result) {
        const status = result.status === 'pass' ? '✅' : '❌';
        console.log(`${status} ${result.name}`);
      },
    
      onTestRunEnd({ results }) {
        const passed = results.filter((r) => r.status === 'pass').length;
        const failed = results.filter((r) => r.status === 'fail').length;
        console.log(`\n📊 ${passed} passed, ${failed} failed`);
      },
    };

    文件输出报告器

    import { writeFileSync } from 'node:fs';
    import type { Reporter } from '@rstest/core';
    
    const jsonReporter: Reporter = {
      onTestRunEnd({ results }) {
        const report = {
          timestamp: new Date().toISOString(),
          results: results.map((r) => ({
            path: r.testPath,
            status: r.status,
            duration: r.duration,
            errors: r.errors,
          })),
        };
    
        writeFileSync('test-report.json', JSON.stringify(report, null, 2));
      },
    };

    读取运行时 metadata

    import type { Reporter } from '@rstest/core';
    
    const metadataReporter: Reporter = {
      onTestCaseResult(result) {
        console.log(result.name, result.meta);
      },
    
      onTestSuiteResult(result) {
        console.log(result.name, result.meta);
      },
    
      onTestFileResult(result) {
        console.log(result.testPath, result.meta);
      },
    };