覆盖率
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-v8
npm i -D @vitest/coverage-istanbul
V8 提供者
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
选项,将覆盖率报告放置在子目录中。



