测试覆盖率
测试覆盖率是一种衡量现有测试是否完全覆盖代码的实践。这意味着揭示当前未被测试的区域,例如:条件、逻辑分支、函数和变量。
覆盖率测试根据一套行业公认的最佳实践来检查检测的代码。它们充当 QA 的最后一道防线,以提高测试套件的质量。
使用 Storybook Test
当您使用 Test 插件 运行组件测试时,该插件由 Vitest 提供支持,它可以生成覆盖率报告。结果在测试模块中进行汇总,显示您的已测试 stories 覆盖的语句百分比。
设置
覆盖率已包含在 Test 插件中,启用后,在为您的项目运行组件测试时将进行计算。要启用覆盖率,请按测试模块中的编辑按钮(铅笔图标)并将覆盖率切换为开启
在计算覆盖率之前,您可能需要安装与您的 覆盖率提供程序 对应的支持包
# For v8
npm install --save-dev @vitest/coverage-v8
# For istanbul
npm install --save-dev @vitest/coverage-istanbul
此外(在 Vitest 3.0.0 发布之前),生成的覆盖率报告将包括 stories 文件本身以及来自您构建的 Storybook 应用程序的输出。这是具有误导性的,应该排除它们。为此,您可以将以下内容添加到您的 Vitest 配置中
import { coverageConfigDefaults, defineConfig } from 'vitest/config';
export default defineConfig({
// ...
test: {
coverage: {
// 👇 Add this
exclude: [
...coverageConfigDefaults.exclude,
'**/.storybook/**',
// 👇 This pattern must align with the `stories` property of your `.storybook/main.ts` config
'**/*.stories.*',
// 👇 This pattern must align with the output directory of `storybook build`
'**/storybook-static/**',
],
}
}
})
用法
由于覆盖率已内置到 Test 插件中,因此您可以在运行测试的任何地方使用它。
Storybook UI
当您在 Storybook UI 中启用覆盖率时,覆盖率报告将在您运行测试后生成并在测试模块中进行汇总。您可以看到您的已测试 stories 覆盖的语句百分比,以及覆盖率是否满足 水印线。
此外,完整的覆盖率报告将在您运行的 Storybook 的 /coverage/index.html
路由中提供。
重要的是要理解,Storybook UI 中报告的覆盖率有三个重要的限制
- 覆盖率是使用您编写的 stories 计算的,而不是整个代码库。换句话说,它不会包含任何其他 Vitest 测试。
- 覆盖率只能针对项目中的所有 stories 进行计算,而不能针对单个 story 或一组 stories。
- 在激活监听模式时,不会计算覆盖率。启用覆盖率后,启用监听模式将禁用覆盖率。
CLI
与 Storybook Test 的其余部分一样,覆盖率构建于 Vitest 之上。这意味着您可以使用 Vitest CLI 生成覆盖率报告。
假设您使用如下所示的包脚本运行测试
{
"scripts": {
"test-storybook": "vitest --project=storybook"
}
}
那么您可以使用以下命令生成覆盖率报告
npm run test-storybook -- --coverage
覆盖率报告将保存到项目中的 配置的覆盖率报告目录(默认为 ./coverage
)。
上面的命令将仅计算您编写的 stories 的覆盖率,而不是整个代码库的覆盖率。
由于在考虑项目中的所有测试时,覆盖率最准确,因此您还可以使用以下命令为项目中的所有测试运行覆盖率
npx vitest --coverage
编辑器扩展
覆盖率也可通过 Vitest 的 IDE 集成获得。您可以直接在编辑器中计算和显示覆盖率结果。
请注意,此覆盖率将包括项目中的所有测试,而不仅仅是您编写的 stories。
CI
要在 CI 管道中生成覆盖率报告,您可以使用 CLI。
例如,这是一个简化的 GitHub Actions 工作流程,用于运行您的测试并生成覆盖率报告
name: Storybook Tests
on: push
jobs:
test:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20.x'
- name: Install dependencies
run: yarn
- name: Run Storybook tests
run: yarn test-storybook --coverage
有关 CI 中测试的更多信息,请参阅 Test 插件文档。
配置
覆盖率提供程序
您可以通过在 Vitest 配置中设置 coverage.provider
选项来选择使用哪个提供程序,v8(默认)或 Istanbul,用于覆盖率计算
import { defineConfig } from 'vitest/config';
export default defineConfig({
// ...
test: {
// ...
coverage: {
// ...
provider: 'istanbul', // 'v8' is the default
},
},
});
水印线
覆盖率提供程序都支持 水印线,即覆盖率的阈值。低水印线是通过测试所需的最低覆盖率,而高水印线是被认为良好的最低覆盖率。介于低水印线和高水印线之间的覆盖率百分比将被认为是可接受的,但不是理想的。
在测试模块中,覆盖率摘要将显示您的已测试 stories 覆盖的语句百分比,以及覆盖率是否满足水印线。低于低水印线,图标将显示为红色;介于低水印线和高水印线之间,图标将显示为橙色;高于高水印线,图标将显示为绿色。
要配置水印线,您可以调整 Vitest 配置
import { defineConfig } from 'vitest/config';
export default defineConfig({
// ...
test: {
// ...
coverage: {
// ...
watermarks: {
// These are the default values
statements: [50, 80],
},
},
},
});
其他配置
您可以在 Vitest 文档中找到更多关于覆盖率的配置选项。
在 Storybook UI 中计算覆盖率时,以下选项始终被忽略
enabled
clean
cleanOnRerun
reportOnFailure
reporter
reportsDirectory
使用覆盖率插件
观看视频教程
Storybook 还提供了一个 覆盖率插件。它由 Istanbul 提供支持,Istanbul 允许为 JavaScript 生态系统中常用的框架和构建器进行开箱即用的代码检测。
设置
覆盖率插件旨在与现代测试工具(例如 Playwright)协同工作,可自动检测您的代码并生成代码覆盖率数据。为了获得最佳体验,我们建议将 测试运行器 与覆盖率插件一起使用以运行您的测试。
运行以下命令安装插件。
npx storybook@latest add @storybook/addon-coverage
使用以下命令启动您的 Storybook
npm run storybook
最后,打开一个新的终端窗口并使用以下命令运行测试运行器
npm run test-storybook -- --coverage
配置
默认情况下,@storybook/addon-coverage
为 Storybook 提供零配置支持,并通过 istanbul-lib-instrument
检测 Webpack 的代码,或通过 vite-plugin-istanbul
检测 Vite 的代码。但是,您可以扩展 Storybook 配置文件(即 .storybook/main.js|ts
)并为插件提供其他选项。下面列出了按构建器划分的可用选项以及如何使用它们的示例。
// For Vite support add the following import
// import type { AddonOptionsVite } from '@storybook/addon-coverage';
import type { AddonOptionsWebpack } from '@storybook/addon-coverage';
// Replace your-framework with the framework and builder you are using (e.g., react-webpack5, vue3-webpack5)
import type { StorybookConfig } from '@storybook/your-framework';
const coverageConfig: AddonOptionsWebpack = {
istanbul: {
include: ['**/stories/**'],
exclude: ['**/exampleDirectory/**'],
},
};
const config: StorybookConfig = {
stories: [],
addons: [
// Other Storybook addons
{
name: '@storybook/addon-coverage',
options: coverageConfig,
},
],
};
export default config;
Vite 选项
选项 | 描述 | 类型 |
---|---|---|
checkProd | 配置插件以跳过生产环境中的检测options: { istanbul: { checkProd: true,}} | boolean |
cwd | 配置覆盖率测试的工作目录。 默认为 process.cwd() options: { istanbul: { cwd: process.cwd(),}} | string |
cypress | 将 VITE_COVERAGE 环境变量替换为 CYPRESS_COVERAGE 。需要 Cypress 的 代码覆盖率 options: { istanbul: { cypress: true,}} | boolean |
exclude | 使用提供的要从覆盖率中排除的文件或目录列表覆盖默认排除列表options: { istanbul: { exclude: ['**/stories/**'],}} | Array<String> 或 string |
extension | 使用提供的要包含在覆盖率中的文件扩展名列表扩展默认扩展列表options: { istanbul: { extension: ['.js', '.cjs', '.mjs'],}} | Array<String> 或 string |
forceBuildInstrument | 配置插件以在构建模式下添加检测options: { istanbul: { forceBuildInstrument: true,}} | boolean |
include | 选择要收集覆盖率的文件options: { istanbul: { include: ['**/stories/**'],}} | Array<String> 或 string |
nycrcPath | 定义现有 nyc 配置文件的相对路径options: { istanbul: { nycrcPath: '../nyc.config.js',}} | string |
requireEnv | 通过授予对 env 变量的访问权限来覆盖 VITE_COVERAGE 环境变量的值options: { istanbul: { requireEnv: true,}} | boolean |
Webpack 5 选项
选项 | 描述 | 类型 |
---|---|---|
autoWrap | 通过将程序代码包装在一个函数中来提供对顶层 return 语句的支持options: { istanbul: { autoWrap: true,}} | boolean |
compact | 压缩检测代码的输出。对调试很有用options: { istanbul: { compact: false,}} | boolean |
coverageVariable | 定义 Istanbul 将用于存储覆盖率结果的全局变量名options: { istanbul: { coverageVariable: '__coverage__',}} | string |
cwd | 配置覆盖率测试的工作目录。 默认为 process.cwd() options: { istanbul: { cwd: process.cwd(),}} | string |
debug | 启用调试模式以获取检测过程中的其他日志信息options: { istanbul: { debug: true,}} | boolean |
esModules | 启用对 ES Module 语法 的支持options: { istanbul: { esModules: true,}} | boolean |
exclude | 使用提供的要从覆盖率中排除的文件或目录列表覆盖默认排除列表options: { istanbul: { exclude: ['**/stories/**'],}} | Array<String> 或 string |
extension | 使用提供的要包含在覆盖率中的文件扩展名列表扩展默认扩展列表options: { istanbul: { extension: ['.js', '.cjs', '.mjs'],}} | Array<String> 或 string |
include | 选择要收集覆盖率的文件options: { istanbul: { include: ['**/stories/**'],}} | Array<String> 或 string |
nycrcPath | 定义现有 nyc 配置文件的相对路径options: { istanbul: { nycrcPath: '../nyc.config.js',}} | string |
preserveComments | 在检测代码中包含注释options: { istanbul: { preserveComments: true,}} | boolean |
produceSourceMap | 配置 Istanbul 为检测代码生成源地图options: { istanbul: { produceSourceMap: true,}} | boolean |
sourceMapUrlCallback | 定义在生成源地图时使用文件名和源地图 URL 调用的回调函数options: { istanbul: { sourceMapUrlCallback: (filename, url) => {},}} | function |
其他覆盖率报告工具呢?
开箱即用,代码覆盖率测试与 Storybook 的测试运行器和 @storybook/addon-coverage
无缝协作。但是,这并不意味着您不能使用其他报告工具(例如 Codecov)。例如,如果您正在使用 LCOV,则可以使用生成的输出(在 coverage/storybook/coverage-storybook.json
中)并使用以下命令创建您自己的报告
npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook
故障排除
在其他框架中运行测试覆盖率
如果您打算在具有特殊文件的框架(如 Vue 3 或 Svelte)中运行覆盖率测试,则需要调整配置并启用所需的文件扩展名。例如,如果您正在使用 Vue,则需要将以下内容添加到您的 nyc 配置文件(即 .nycrc.json
或 nyc.config.js
)
export default {
// Other configuration options
extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue'],
};
覆盖率插件不支持优化的构建
如果您使用 --test
标志生成了针对性能优化的生产构建,并且您正在使用覆盖率插件针对您的 Storybook 运行测试,您可能会遇到覆盖率插件不检测您的代码的情况。这是由于该标志的工作方式,因为它会删除对性能有影响的插件(例如 Docs
、覆盖率插件)。要解决此问题,您需要调整 Storybook 配置文件(即 .storybook/main.js|ts
)并包含 disabledAddons
选项,以允许插件以牺牲较慢的构建速度为代价来运行测试。
// Replace your-framework with the framework you are using (e.g., react-webpack5, vue3-vite)
import type { StorybookConfig } from '@storybook/your-framework';
const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
],
build: {
test: {
disabledAddons: ['@storybook/addon-docs', '@storybook/addon-essentials/docs'],
},
},
};
export default config;
覆盖率插件不支持检测代码
由于 覆盖率插件 基于 Webpack5 加载器和 Vite 插件进行代码检测,因此不依赖这些库的框架(例如,使用 Webpack 配置的 Angular)将需要额外的配置才能启用代码检测。在这种情况下,您可以参考以下 存储库 以获取更多信息。
了解其他 UI 测试