close

coverage

  • 类型:
type CoverageOptions = {
  enabled?: boolean;
  provider?: 'istanbul' | 'v8';
  include?: string[];
  changed?: boolean | string;
  exclude?: string[];
  reporters?: (keyof ReportOptions | ReportWithOptions)[];
  reportsDirectory?: string;
  reportOnFailure?: boolean;
  clean?: boolean;
  allowExternal?: boolean;
  thresholds?: CoverageThresholds;
};
  • 默认值: undefined

收集测试覆盖率信息并生成覆盖率报告。

$ npx rstest --coverage

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |
 index.ts |     100 |      100 |     100 |     100 |
----------|---------|----------|---------|---------|-------------------

选项

enabled

  • 类型: boolean
  • 默认值: false
  • CLI: --coverage, --coverage=false, --no-coverage

启用或禁用测试覆盖率收集。

CLI
rstest.config.ts
npx rstest --coverage

provider

  • 类型: 'istanbul' | 'v8'
  • 默认值: 'istanbul'
  • CLI: --coverage.provider <provider>

选择覆盖率收集方式。Rstest 同时支持 istanbulv8。如果你更关注基于 SWC 插桩的转换性能和稳定的 Istanbul 语义,可以选择 istanbul;如果开启 coverage 后主要受内存压力影响,可以选择 v8,它通常有更低的运行时内存占用,因为不会为每个执行模块注入覆盖率计数器。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    provider: 'v8',
  },
});

Istanbul provider

Istanbul 是一个广泛使用的 JavaScript 代码覆盖率分析工具,它通过插桩的方式来收集代码覆盖率信息。

要启用 istanbul 覆盖率,需要先安装 @rstest/coverage-istanbul

npm
yarn
pnpm
bun
deno
npm add @rstest/coverage-istanbul -D

@rstest/coverage-istanbulswc-plugin-coverage-instrument 提供支持。Rstest 会在 transform 阶段通过 SWC 完成插桩,因此该 provider 具有较好的 transform 性能。但插桩后的代码会在执行时保留用于收集覆盖率的计数器,因此运行时内存占用可能更高。

V8 provider

v8 provider 基于 Node.js inspector precise coverage 收集数据,并重映射为 Istanbul 格式。相比 Istanbul 插桩,V8 coverage 通常有更低的运行时内存占用,因为执行模块中不需要额外的覆盖率计数器。它更适合大型非 Browser Mode 测试套件中 coverage 内存压力明显的场景。

要启用 V8 覆盖率,需要先安装 @rstest/coverage-v8

npm
yarn
pnpm
bun
deno
npm add @rstest/coverage-v8 -D

由于底层覆盖率数据来自 Node.js / V8 本身,重映射后的 branch 覆盖率和未覆盖行信息在不同 Node.js 或 V8 版本之间可能存在轻微差异。

v8 provider 仅适用于非 Browser Mode 测试执行,包括 nodejsdomhappy-dom 测试环境。Browser Mode 不使用 inspector coverage,因此浏览器 coverage 应使用默认的 istanbul provider。

include

  • 类型: string[]
  • 默认值: undefined

对匹配 glob 规则的文件进行测试覆盖率收集。

默认情况下,Rstest 会收集已测试文件的覆盖率。如果你希望在覆盖率报告中包含未测试的文件,可以使用 include 选项指定要包含的文件或模式。

需要注意的是,这里应使用标准 glob 语法。匹配单个扩展名时,请写 src/**/*.ts,不要写 src/**/*.{ts}。底层 glob 库会把单元素花括号按字面量处理,因此 src/**/*.{ts} 不会匹配 .ts 文件。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    include: ['src/**/*.{js,jsx,ts,tsx}'],
  },
});

changed

  • 类型: boolean | string
  • 默认值: undefined
  • CLI: --coverage.changed, --coverage.changed=<commit>

只收集当前 Git 仓库中变更文件的覆盖率。你也可以传入 commit 或 branch,收集从该 ref 以来变更文件的覆盖率。

这个选项既可以写在配置里,也可以通过 CLI 使用:

  • 当你希望在项目里长期保持一个默认行为时,使用 rstest.config.ts 中的 coverage.changed,例如在 CI 中。
  • 当你只想临时跑一次时,使用 --coverage.changed

coverage.changed 只控制覆盖率报告范围,不会改变要运行的测试。

也就是说,下面两个命令的效果并不一样:

npx rstest run --changed
npx rstest run --coverage.changed
  • --changed 会改变测试选择范围,只运行与变更文件相关的测试。
  • --coverage.changed 会保持正常的测试选择范围,只把覆盖率报告限制在变更的源码文件上。

--changed 的关系

  • 当使用 --changed 且没有配置 coverage.changed 时,覆盖率报告会继承 --changed 收集到的变更文件。
  • 如果 --changed 命中了 forceRerunTriggers,Rstest 会回退为运行完整测试套件。此时覆盖率默认也会回到完整范围,除非显式启用了 coverage.changed
  • 如果你想用 --changed 缩小测试执行范围,但仍然保留完整覆盖率报告,可以在配置中将 coverage.changed 设为 false

常见用法如下:

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    changed: 'origin/main',
  },
});

上面的配置不会改变测试执行范围,但会让覆盖率报告只包含相对 origin/main 有变更的文件。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    changed: 'HEAD~1',
  },
});
npx rstest run --changed --coverage
npx rstest run --coverage.changed
npx rstest run --coverage.changed=HEAD~1
npx rstest run --coverage.changed=origin/main
  • npx rstest run --changed --coverage 会运行变更相关测试,并且默认把覆盖率也限制到同一批变更文件。
  • npx rstest run --coverage.changed=HEAD~1 会执行正常测试集,但只报告相对 HEAD~1 以来变更文件的覆盖率。
  • npx rstest run --coverage.changed=origin/main 适合在功能分支上使用:它仍然会执行正常测试集,但覆盖率报告只包含相对 origin/main 变更过的文件。

exclude

  • 类型: string[]
  • 默认值:
[
  '**/node_modules/**',
  '**/test/**',
  '**/__tests__/**',
  '**/__mocks__/**',
  '**/*.d.ts',
  '**/*.{test,spec}.[jt]s',
  '**/*.{test,spec}.[cm][jt]s',
  '**/*.{test,spec}.[jt]sx',
  '**/*.{test,spec}.[cm][jt]sx',
];

匹配 glob 规则的文件将从测试覆盖率收集中排除。自定义配置将与默认值合并。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    exclude: ['**/node_modules/**', '**/dist/**'],
  },
});

reporters

  • 类型: (ReporterName | [ReporterName, ReporterOptions>])[]
  • 默认值: ['text', 'html', 'clover', 'json']

用于覆盖率收集的报告器。每个报告器可以是字符串(报告器名称)或包含报告器名称及其选项的元组。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reporters: [
      'html',
      ['text', { skipFull: true }],
      ['json', { file: 'coverage-final.json' }],
    ],
  },
});

reportsDirectory

  • 类型: string
  • 默认值: './coverage'

存储覆盖率报告的目录。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    reportsDirectory: './coverage-reports',
  },
});

reportOnFailure

  • 类型: boolean
  • 默认值: false

在测试失败时是否生成覆盖率报告并检查阈值。

rstest.config.ts
import { defineConfig } from '@rstest/core';
export default defineConfig({
  coverage: {
    enabled: true,
    reportOnFailure: true,
  },
});

allowExternal

  • 类型: boolean
  • 默认值: false
  • CLI: --coverage.allowExternal

是否收集项目根目录之外的源文件的覆盖率。这在 monorepo 中非常有用,例如测试文件导入了来自同级工作区包的模块。

默认情况下,Rstest 会从覆盖率报告中排除项目根目录之外的文件,这与 Jest 和 Vitest 的行为一致。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    allowExternal: true,
  },
});

clean

  • 类型: boolean
  • 默认值: true

是否在运行测试之前清理覆盖率目录。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    clean: true,
  },
});

thresholds

  • 类型:
type CoverageThreshold = {
  statements?: number;
  functions?: number;
  branches?: number;
  lines?: number;
};

type CoverageThresholds = CoverageThreshold & {
  /** 为匹配的文件指定覆盖率阈值 */
  [glob: string]: CoverageThreshold;
};
  • 默认值: undefined

设置最低代码覆盖率要求。你可以为语句、函数、分支和行覆盖率设置阈值。

当阈值设置为正数时,表示所需的最低百分比。当阈值设置为负数时,表示允许未覆盖的最大数量。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      statements: 80,
      functions: 80,
      branches: 80,
      lines: -10,
    },
  },
});

当代码覆盖率低于指定阈值时,测试将失败并输出如下错误信息:

Error: Coverage for statements 75% does not meet global threshold 80%
Error: Coverage for functions 75% does not meet global threshold 80%
Error: Coverage for branches 75% does not meet global threshold 80%
Error: Uncovered lines 20 exceeds maximum global threshold allowed 10

glob 模式

当指定 glob 模式时,Rstest 将根据匹配的文件模式进行代码覆盖率检查。如果指定的文件路径不存在,则返回错误。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      // 为匹配的文件指定覆盖率阈值
      'src/**': {
        statements: 100,
      },
      'node/**/*.js': {
        statements: 90,
      },
      // 指定所有文件的总阈值
      statements: 80,
    },
  },
});

根据以上配置,rstest 将在以下情况下失败:

  • src/** 匹配到的文件的总语句覆盖率低于 100%。
  • node/**/*.js 匹配到的文件的总语句覆盖率低于 90%。
  • 全局的语句覆盖率低于 80%。

单文件检查

Rstest 支持通过将 perFile 设置为 true 来为每个匹配文件分别检查阈值。

rstest.config.ts
import { defineConfig } from '@rstest/core';

export default defineConfig({
  coverage: {
    enabled: true,
    thresholds: {
      'src/**': {
        statements: 90,
        perFile: true,
      },
    },
  },
});