覆盖率
Vitest 通过 v8 支持原生代码覆盖,并通过 istanbul 支持代码插桩覆盖。
覆盖率提供者
v8 和 istanbul 的支持都是可选的。默认情况下,Vitest 会使用 v8。
你可以通过将 test.coverage.provider 设置为 v8 或 istanbul 来选择覆盖率工具:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'istanbul', // 或 'v8'
},
},
});当你启动 Vitest 进程时,它会自动提示并引导你安装相应的支持包。
或者,你也可以选择手动安装它们:
npm i -D @vitest/coverage-v8npm i -D @vitest/coverage-istanbulV8 提供者
INFO
以下对 V8 覆盖率的描述仅适用于 Vitest,不适用于其他测试运行器。 自 v3.2.0 起,Vitest 已对 V8 覆盖率采用 基于 AST 的覆盖率重映射,这会生成与 Istanbul 相同的覆盖率报告。
这使得用户能够兼顾 V8 覆盖率的速度和 Istanbul 覆盖率的准确性。
默认情况下,Vitest 使用 'v8' 覆盖率提供者。 此提供者需要基于 V8 引擎 的 JavaScript 运行时,例如 NodeJS、Deno 或任何基于 Chromium 的浏览器(如 Google Chrome)。
覆盖率收集是在运行时进行的,通过使用 node:inspector 指示 V8,并在浏览器中使用 Chrome DevTools Protocol 来实现。用户的源文件无需任何预插桩步骤即可直接执行。
- ✅ 推荐使用的选项
- ✅ 无需预转译步骤。测试文件可以原封不动地执行。
- ✅ 执行速度比 Istanbul 快。
- ✅ 内存占用比 Istanbul 低。
- ✅ 覆盖率报告的准确性与 Istanbul 相当(自 Vitest
v3.2.0起)。 - ⚠️ 在某些情况下,例如加载大量不同模块时,可能会比 Istanbul 慢。V8 不支持将覆盖率收集限制到特定模块。
- ⚠️ V8 引擎存在一些次要限制。请参阅
ast-v8-to-istanbl| 限制。 - ❌ 不适用于不使用 V8 的环境(例如 Firefox 或 Bun),也不适用于不通过分析器暴露 V8 覆盖率的环境(例如 Cloudflare Workers)。
Istanbul 提供者
Istanbul 代码覆盖率工具 自 2012 年以来一直存在,并经过了充分的实战验证。 此提供者适用于任何 JavaScript 运行时,因为覆盖率跟踪是通过对用户源文件进行插桩实现的。
实际上,对源文件进行插桩意味着在用户文件中注入额外的 JavaScript 代码:
// 分支和函数覆盖计数器的简化示例
const coverage = {
branches: { 1: [0, 0] },
functions: { 1: 0 },
}
export function getUsername(id) {
// 此函数被调用时,函数覆盖率增加
coverage.functions['1']++;
if (id == null) {
// 此分支被执行时,分支覆盖率增加
coverage.branches['1'][0]++;
throw new Error('User ID is required');
}
// 当 if 语句条件不满足时,隐式 else 覆盖率会增加
coverage.branches['1'][1]++;
return database.getUser(id);
}
globalThis.__VITEST_COVERAGE__ ||= {};
globalThis.__VITEST_COVERAGE__[filename] = coverage; - ✅ 适用于任何 JavaScript 运行时
- ✅ 广泛使用并经过 13 年多的实战检验。
- ✅ 在某些情况下,可能会比 V8 快。覆盖率插桩可以限制到特定文件,而 V8 则会对所有模块进行插桩。
- ❌ 需要预先进行插桩步骤
- ❌ 由于插桩开销,执行速度会比 V8 慢
- ❌ 插桩会导致文件大小增加
- ❌ 内存占用比 V8 高
覆盖率设置
TIP
建议始终在配置文件中定义 coverage.include 选项。 这有助于 Vitest 减少 coverage.all 所包含的文件数量。
要启用覆盖率测试,你可以在 CLI 中传递 --coverage 标志。 默认情况下,将使用 ['text', 'html', 'clover', 'json'] 这些报告器。
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}要进行配置,请在你的配置文件中设置 test.coverage 选项:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
reporter: ['text', 'json', 'html'],
},
},
});自定义覆盖率报告器
你可以通过在 test.coverage.reporter 中指定包名或绝对路径来使用自定义覆盖率报告器:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
reporter: [
// 使用 NPM 包名指定报告器
['@vitest/custom-coverage-reporter', { someOption: true }],
// 使用本地路径指定报告器
'/absolute/path/to/custom-reporter.cjs',
],
},
},
});自定义报告器由 Istanbul 加载,并且必须实现其报告器接口。请参阅 内置报告器的实现 以供参考。
const { ReportBase } = require('istanbul-lib-report');
module.exports = class CustomReporter extends ReportBase {
constructor(opts) {
super();
// 配置中传递的选项可在此处使用
this.file = opts.file;
}
onStart(root, context) {
this.contentWriter = context.writer.writeFile(this.file);
this.contentWriter.println('自定义覆盖率报告开始');
}
onEnd() {
this.contentWriter.println('自定义覆盖率报告结束');
this.contentWriter.close();
}
};自定义覆盖率提供者
也可以通过将 test.coverage.provider 设置为 'custom' 来提供自定义覆盖率提供者:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
coverage: {
provider: 'custom',
customProviderModule: 'my-custom-coverage-provider',
},
},
});自定义提供者需要一个 customProviderModule 选项,该选项指定了加载 CoverageProviderModule 的模块名称或路径。它必须默认导出一个实现 CoverageProviderModule 接口的对象:
import type {
CoverageProvider,
CoverageProviderModule,
ResolvedCoverageOptions,
Vitest,
} from 'vitest';
const CustomCoverageProviderModule: CoverageProviderModule = {
getProvider(): CoverageProvider {
return new CustomCoverageProvider();
},
// 实现 CoverageProviderModule 的其他方法...
};
class CustomCoverageProvider implements CoverageProvider {
name = 'custom-coverage-provider';
options!: ResolvedCoverageOptions;
initialize(ctx: Vitest) {
this.options = ctx.config.coverage;
}
// 实现 CoverageProvider 的其他方法...
}
export default CustomCoverageProviderModule;请参阅类型定义了解更多详细信息。
更改默认覆盖率文件夹位置
生成覆盖率报告时,会在项目根目录中创建一个 coverage 文件夹。如果你想将其移动到其他目录,请在 vitest.config.js 文件中使用 test.coverage.reportsDirectory 属性。
import { defineConfig } from 'vite';
export default defineConfig({
test: {
coverage: {
reportsDirectory: './tests/unit/coverage',
},
},
});忽略代码
两种覆盖率提供者都有各自忽略覆盖率报告中代码的方式:
使用 TypeScript 时,源代码会通过 esbuild 进行转译,这会移除源代码中的所有注释 (esbuild#516)。 被视为 合法注释 的注释会被保留。
你可以在忽略提示中加入 @preserve 关键字。 请注意,这些忽略提示现在也可能会被包含在最终的生产构建中。
-/* istanbul ignore if */
+/* istanbul ignore if -- @preserve */
if (condition) {
-/* v8 ignore if */
+/* v8 ignore if -- @preserve */
if (condition) {其他选项
查看所有可配置的覆盖率选项,请参阅 覆盖率配置参考。
覆盖率性能
如果你的项目代码覆盖率生成速度较慢,请参阅 分析测试性能 | 代码覆盖率。
Vitest UI
你可以在 Vitest UI 中查看覆盖率报告。
当明确启用覆盖率且存在 HTML 覆盖率报告器时,Vitest UI 将启用覆盖率报告功能,否则该功能将不可用:
- 在你的配置文件中将
coverage.enabled设置为true,或使用--coverage.enabled=true标志运行 Vitest - 将
html添加到coverage.reporter列表中;你还可以启用subdir选项,将覆盖率报告放置在子目录中。



