Docs
Storybook Docs

测试覆盖率

测试覆盖率是测量现有测试是否完全覆盖代码的做法。这意味着可以发现当前未测试的区域,例如:条件、逻辑分支、函数和变量。

¥Test coverage is the practice of measuring whether existing tests fully cover your code. That means surfacing areas which aren't currently being tested, such as: conditions, logic branches, functions and variables.

覆盖率测试根据一组行业认可的最佳实践检查已检测的代码。它们充当 QA 的最后一行,以提高测试套件的质量。

¥Coverage tests examine the instrumented code against a set of industry-accepted best practices. They act as the last line of QA to improve the quality of your test suite.

使用 Storybook 测试

¥With Storybook Test

使用由 Vitest 提供支持的 测试插件 运行组件测试时,它可以生成代码覆盖率报告。测试模块会汇总测试结果,显示测试用例覆盖的语句百分比。

¥When you run component tests with the Test addon, which is powered by Vitest, it can generate a coverage report. The result is summarized in the testing module, showing the percentage of statements covered by your tested stories.

Screenshot of test module, expanded, showing coverage result

设置

¥Set up

测试插件中包含代码覆盖率功能,启用后,将在运行项目组件测试时计算代码覆盖率。要启用代码覆盖率,请在测试模块中按编辑按钮(铅笔图标)并启用代码覆盖率:

¥Coverage is included in the Test addon and, when enabled, will be calculated when running component tests for your project. To enable coverage, press the edit button (pencil icon) in the testing module and toggle coverage on:

Screenshot of test module, expanded, showing coverage toggle

在计算覆盖率之前,你可能需要安装与你的 覆盖率提供程序 对应的支持包:

¥Before coverage can be calculated, you may need to install a support package corresponding to your coverage provider:

# For v8
npm install --save-dev @vitest/coverage-v8
 
# For istanbul
npm install --save-dev @vitest/coverage-istanbul

此外(在 Vitest 3.0.0 发布之前),生成的覆盖率报告将包含故事文件本身以及已构建的 Storybook 应用的输出。这具有误导性,应该将其排除在外。为此,你可以将以下内容添加到 Vitest 配置中:

¥Additionally (until Vitest 3.0.0 is released), the generated coverage report will include the stories files themselves and output from your built Storybook application. This is misleading and they should be excluded. To do this, you can add the following to your Vitest config:

vitest.config.ts
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/**',
       ], 
    }
  }
})

用法

¥Usage

由于测试插件内置了代码覆盖率功能,你可以在运行测试的任何位置使用它。

¥Because coverage is built into the Test addon, you can use it everywhere you run your tests.

Storybook UI

在 Storybook UI 中启用代码覆盖率后,运行测试后,代码覆盖率报告将在测试模块中生成并汇总。你可以查看测试故事覆盖的语句百分比,以及覆盖率是否符合 watermarks 的要求。

¥When you enable coverage in the Storybook UI, the coverage report will be generated and summarized in the testing module after you run your tests. You can see the percentage of statements covered by your tested stories, as well as whether the coverage meets the watermarks.

此外,完整的覆盖率报告将通过你正在运行的 Storybook 的 /coverage/index.html 路由提供。

¥Additionally, the full coverage report will be served at the /coverage/index.html route of your running Storybook.

请务必了解 Storybook UI 中报告的覆盖率有三个重要限制:

¥It's important to understand that the coverage reported in the Storybook UI has three important limitations:

  1. 覆盖率是使用你编写的故事计算的,而不是整个代码库。换句话说,它不会包含任何其他 Vitest 测试。

    ¥Coverage is calculated using the stories you've written, not the entire codebase. In other words, it will not include any other Vitest tests.

  2. 覆盖率只能针对项目中的所有故事进行计算,而不能针对单个故事或一组故事进行计算。

    ¥Coverage can only be calculated for all stories in your project, not for an individual story or a group of stories.

  3. 激活监视模式时不会计算覆盖率。启用覆盖率后,启用监视模式将禁用覆盖率。

    ¥Coverage is not calculated while watch mode is activated. When coverage is enabled, enabling watch mode will disable coverage.

CLI

与 Storybook Test 的其他功能一样,覆盖率测试基于 Vitest 构建。这意味着你可以使用 Vitest CLI 生成覆盖率报告。

¥Like the rest of Storybook Test, coverage is built on top of Vitest. Which means you can generate a coverage report using the Vitest CLI.

假设你使用如下包脚本运行测试:

¥Assuming you run your tests with a package script like this:

package.json
{
  "scripts": {
    "test-storybook": "vitest --project=storybook"
  }
}

然后,你可以使用以下命令生成覆盖率报告:

¥Then you can generate a coverage report with:

npm run test-storybook -- --coverage

覆盖率报告将保存到项目中的 已配置覆盖率报告目录(默认为 ./coverage)。

¥The coverage report will be saved to the configured coverage reports directory (./coverage, by default) in your project.

上述命令将仅计算你编写的故事的覆盖率,而不是整个代码库。

¥The above command will only calculate coverage for the stories you've written, not the entire codebase.

由于覆盖率在涵盖项目所有测试时最为准确,因此你也可以使用以下命令对项目中的所有测试进行覆盖率计算:

¥Because coverage is most accurate when accounting for all tests in your project, you can also run coverage for all tests in your project with:

npx vitest --coverage

编辑器扩展

¥Editor extension

Vitest 的 IDE 集成 也提供覆盖率计算功能。你可以直接在编辑器中计算并显示覆盖率结果。

¥Coverage is also available through Vitest's IDE integrations. You can calculate and display coverage results directly in your editor.

Screenshot of test coverage in VSCode

请注意,此覆盖范围将涵盖你项目中的所有测试,而不仅仅是你编写的故事。

¥Note that this coverage will include all tests in your project, not just the stories you've written.

CI

要在 CI 管道中生成覆盖率报告,你可以使用 CLI

¥To generate coverage reports in your CI pipeline, you can use the CLI.

例如,这是一个简化的 GitHub Actions 工作流程,用于运行测试并生成覆盖率报告:

¥For example, here's a simplified GitHub Actions workflow that runs your tests and generates a coverage report:

.github/workflows/test-storybook.yml
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 中进行测试的更多信息,请参阅 测试插件文档

¥For more on testing in CI, see the Test addon docs.

配置

¥Configuration

覆盖率提供程序

¥Coverage provider

你可以通过在 Vitest 配置中设置 coverage.provider 选项来选择使用哪个提供程序(v8(默认)或 伊斯坦布尔)进行覆盖率计算:

¥You can choose which provider, v8 (default) or Istanbul, to use for coverage calculation by setting the coverage.provider option in your Vitest config:

vitest.config.ts
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  // ...
  test: {
    // ...
    coverage: {
      // ...
      provider: 'istanbul', // 'v8' is the default
    },
  },
});

水印

¥Watermarks

两种覆盖率提供程序都支持 watermarks,这是覆盖率的阈值。低水位线是通过测试所需的最低覆盖率,高水位线是被视为良好所需的最低覆盖率。介于低水位线和高水位线之间的覆盖率百分比被认为是可接受的,但并非理想值。

¥Both coverage providers support watermarks, which are threshold values for coverage. The low watermark is the minimum coverage required to pass the test, and the high watermark is the minimum coverage required to be considered good. A coverage percentage between the low and high watermarks will be considered acceptable but not ideal.

在测试模块中,覆盖率摘要将显示被测故事覆盖的语句百分比,以及覆盖率是否达到水印。低于低水位线时,图标为红色;介于低水位线和高水位线之间时,图标为橙色;高于高水位线时,图标为绿色。

¥In the testing module, the coverage summary will show the percentage of statements covered by your tested stories, as well as whether the coverage meets the watermarks. Below the low watermark, the icon will be red, between the low and high watermarks, it will be orange, and above the high watermark, it will be green.

Screenshot of test module, expanded, showing coverage result

要配置水印,你可以调整 Vitest 配置:

¥To configure the watermarks, you can adjust the Vitest config:

vitest.config.ts
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  // ...
  test: {
    // ...
    coverage: {
      // ...
      watermarks: {
        // These are the default values
        statements: [50, 80],
      },
    },
  },
});

附加配置

¥Additional configuration

你可以在 Vitest 文档 中找到更多覆盖范围的配置选项。

¥You can find more configuration options for coverage in the Vitest documentation.

在 Storybook UI 中计算覆盖率时,以下选项始终会被忽略:

¥When calculating coverage in the Storybook UI, the following options are always ignored:

  • enabled

  • clean

  • cleanOnRerun

  • reportOnFailure

  • reporter

  • reportsDirectory

使用 coverage 插件

¥With the coverage addon

Watch a video tutorial

Storybook 也提供了 覆盖插件。它由 伊斯坦布尔 提供支持,伊斯坦布尔 允许为 JavaScript 生态系统中最常用的框架和构建器提供开箱即用的代码检测。

¥Storybook also provides a coverage addon. It is powered by Istanbul, which allows out-of-the-box code instrumentation for the most commonly used frameworks and builders in the JavaScript ecosystem.

设置

¥Set up

覆盖率插件经过精心设计,可与现代测试工具(例如 Playwright)配合使用,可自动检测你的代码并生成代码覆盖率数据。为了获得最佳体验,我们建议将 测试运行器 与 coverage 插件一起使用来运行测试。

¥Engineered to work alongside modern testing tools (e.g., Playwright), the coverage addon automatically instruments your code and generates code coverage data. For an optimal experience, we recommend using the test runner alongside the coverage addon to run your tests.

运行以下命令来安装插件。

¥Run the following command to install the addon.

npx storybook@latest add @storybook/addon-coverage

CLI 的 add 命令可以自动补齐插件的安装和设置。要手动安装,请参阅我们的 documentation 了解如何安装插件。

¥The CLI's add command automates the addon's installation and setup. To install it manually, see our documentation on how to install addons.

使用以下方式启动 Storybook:

¥Start your Storybook with:

npm run storybook

最后,打开一个新的终端窗口并运行测试运行器:

¥Finally, open a new terminal window and run the test-runner with:

npm run test-storybook -- --coverage

Coverage test output

配置

¥Configure

默认情况下,@storybook/addon-coverage 为 Storybook 提供零配置支持,并通过 istanbul-lib-instrument(用于 Webpack)或 vite-plugin-istanbul(用于 Vite)为你的代码提供支持。但是,你可以扩展 Storybook 配置文件(即 .storybook/main.js|ts)并为插件提供其他选项。下面列出了按构建器划分的可用选项及其使用方法示例。

¥By default, the @storybook/addon-coverage offers zero-config support for Storybook and instruments your code via istanbul-lib-instrument for Webpack, or vite-plugin-istanbul for Vite. However, you can extend your Storybook configuration file (i.e., .storybook/main.js|ts) and provide additional options to the addon. Listed below are the available options divided by builder and examples of how to use them.

.storybook/main.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 options
选项描述类型
checkProd配置插件以在生产环境中跳过检测
options: { istanbul: { checkProd: true,}}
boolean
cwd配置覆盖率测试的工作目录。
默认为 process.cwd()
options: { istanbul: { cwd: process.cwd(),}}
string
cypressCYPRESS_COVERAGE 替换 VITE_COVERAGE 环境变量。
需要 Cypress 的 代码覆盖率
options: { istanbul: { cypress: true,}}
boolean
exclude使用提供的要从覆盖范围中排除的文件或目录列表覆盖 默认排除列表
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 options
选项描述类型
autoWrap通过将程序代码封装在函数中来提供对顶层返回语句的支持
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 模块语法的支持
options: { istanbul: { esModules: true,}}
boolean
exclude使用提供的要从覆盖范围中排除的文件或目录列表覆盖 默认排除列表
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配置 Instanbul 以生成已检测代码的源映射
options: { istanbul: { produceSourceMap: true,}}
boolean
sourceMapUrlCallback定义一个回调函数,在生成源映射时使用文件名和源映射 URL 调用
options: { istanbul: { sourceMapUrlCallback: (filename, url) => {},}}
function

其他覆盖率报告工具怎么样?

¥What about other coverage reporting tools?

开箱即用,代码覆盖率测试可与 Storybook 的测试运行器和 @storybook/addon-coverage 无缝协作。但是,这并不意味着你不能使用其他报告工具(例如 Codecov)。例如,如果你正在使用 LCOV,则可以使用生成的输出(在 coverage/storybook/coverage-storybook.json 中)并创建自己的报告:

¥Out of the box, code coverage tests work seamlessly with Storybook's test-runner and the @storybook/addon-coverage. However, that doesn't mean you can't use additional reporting tools (e.g., Codecov). For instance, if you're working with LCOV, you can use the generated output (in coverage/storybook/coverage-storybook.json) and create your own report with:

npx nyc report --reporter=lcov -t coverage/storybook --report-dir coverage/storybook

故障排除

¥Troubleshooting

在其他框架中运行测试覆盖率

¥Run test coverage in other frameworks

如果你打算在具有特殊文件(如 Vue 3 或 Svelte)的框架中运行覆盖率测试,则需要调整配置并启用所需的文件扩展名。例如,如果你使用的是 Vue,则需要将以下内容添加到 nyc 配置文件(即 .nycrc.jsonnyc.config.js)中:

¥If you intend on running coverage tests in frameworks with special files like Vue 3 or Svelte, you'll need to adjust your configuration and enable the required file extensions. For example, if you're using Vue, you'll need to add the following to your nyc configuration file (i.e., .nycrc.json or nyc.config.js):

.nyc.config.js
export default {
  // Other configuration options
  extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.vue'],
};

覆盖率插件不支持优化构建

¥The coverage addon doesn't support optimized builds

如果你使用 --test 标志生成了针对性能优化的生产版本,并且你正在使用覆盖率插件对你的 Storybook 运行测试,则可能会遇到覆盖率插件不会检测你的代码的情况。这是由于标志的工作方式,因为它会删除对性能有影响的附加组件(例如,Docs覆盖插件)。要解决此问题,你需要调整 Storybook 配置文件(即 .storybook/main.js|ts)并包含 disabledAddons 选项以允许插件以较慢的构建速度为代价运行测试。

¥If you generated a production build optimized for performance with the --test flag, and you're using the coverage addon to run tests against your Storybook, you may run into a situation where the coverage addon doesn't instrument your code. This is due to how the flag works, as it removes addons that have an impact on performance (e.g., Docs, coverage addon). To resolve this issue, you'll need to adjust your Storybook configuration file (i.e., .storybook/main.js|ts) and include the disabledAddons option to allow the addon to run tests at the expense of a slower build.

.storybook/main.ts
// 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;

覆盖率插件不支持已检测代码

¥The coverage addon doesn't support instrumented code

由于 覆盖插件 基于 Webpack5 加载器和 Vite 插件进行代码检测,因此不依赖这些库的框架(例如,使用 Webpack 配置的 Angular)将需要额外的配置才能启用代码检测。在这种情况下,你可以参考以下 repository 了解更多信息。

¥As the coverage addon is based on Webpack5 loaders and Vite plugins for code instrumentation, frameworks that don't rely upon these libraries (e.g., Angular configured with Webpack), will require additional configuration to enable code instrumentation. In that case, you can refer to the following repository for more information.

了解其他 UI 测试

¥Learn about other UI tests