For AI agents: the complete documentation index is available at https://rstest.rs/zh/llms.txt, the full documentation bundle is available at https://rstest.rs/zh/llms-full.txt, and this page is available as Markdown at https://rstest.rs/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 接口为测试执行提供生命周期钩子。每个钩子在测试运行期间的具体时间点被调用。

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

    钩子类别

    • 文件级钩子:onTestFileStart、onTestFileReady、onTestFileResult
    • 套件级钩子:onTestSuiteStart、onTestSuiteResult
    • 用例级钩子:onTestCaseStart、onTestCaseResult
    • 运行级钩子:onTestRunStart、onTestRunEnd、onUserConsoleLog、onExit

    基本接口结构

    interface Reporter {
      onTestRunStart?(payload: TestRunStartPayload): MaybePromise<void>;
      onTestFileStart?(test: TestFileInfo): MaybePromise<void>;
      onTestFileReady?(test: TestFileInfo): MaybePromise<void>;
      onTestFileResult?(test: TestFileResult): MaybePromise<void>;
      onTestSuiteStart?(test: TestSuiteInfo): MaybePromise<void>;
      onTestSuiteResult?(result: TestResult): MaybePromise<void>;
      onTestCaseStart?(test: TestCaseInfo): MaybePromise<void>;
      onTestCaseResult?(result: TestResult): MaybePromise<void>;
      onTestRunEnd?(payload: {
        status: TestRunStatus;
        results: TestFileResult[]; // 测试文件结果;watch 模式下是整个会话的累计结果。
        testResults: TestResult[]; // 所有测试用例的结果,不按文件分组。
        summary: {
          tests: {
            total: number; // 测试总数。
            passed: number; // 通过的测试数量。
            failed: number; // 失败的测试数量。
            skipped: number; // 跳过的测试数量。
            todo: number; // todo 测试数量。
            flaky: number; // 重试后通过的测试数量。
          };
          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。
      }): MaybePromise<void>;
      onUserConsoleLog?(log: UserConsoleLog): MaybePromise<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 可以在 onTestFileResult 的 TestFileResult.meta 上读取。

    onTestRunStart

    onTestRunStart 接收的 TestRunStartPayload 包含 files 数组,每个选中文件都有绝对路径 testPath、project 和 testId。watch 模式下,它只包含本轮选中的文件,不是整个会话的累计结果。TestRunStartPayload 可从 @rstest/core 导入。

    • 类型:
    interface TestRunStartPayload {
      files: {
        testPath: string;
        project: string;
        testId: string;
      }[];
    }

    onTestRunEnd

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

    从 v0.13.0 开始,payload 还会带上本次运行的 status;每个取值的含义见 rstest.run。

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

    TestResult

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

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

    TestFileResult

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

    文件耗时

    在 Node pool 中,loadDuration 以毫秒记录 worker 内的产物加载、setup 文件执行,以及测试模块求值与收集耗时。加载失败时也会记录;尚未开始加载或使用 browser mode 时,该字段缺省。现有的 duration 记录测试执行耗时,包含 hooks 和快照收尾。两者之和用于 Node 测试文件排序,不包含 worker 启动、环境初始化、排队和 worker teardown。

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

    示例

    简单自定义报告器

    import type { Reporter } from '@rstest/core';
    
    const simpleReporter: Reporter = {
      onTestFileStart(test) {
        console.log(`📁 ${test.testPath}`);
      },
    
      onTestCaseResult(result) {
        const status = result.status === 'passed' ? '✅' : '❌';
        console.log(`${status} ${result.name}`);
      },
    
      onTestRunEnd({ results }) {
        const passed = results.filter((r) => r.status === 'passed').length;
        const failed = results.filter((r) => r.status === 'failed').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);
      },
    };