文档
Storybook 文档

测试覆盖率

测试覆盖率是一种衡量现有测试是否完全覆盖代码的实践。这意味着揭示当前未被测试的区域,例如:条件、逻辑分支、函数和变量。

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

使用 Storybook Test

当您使用 Test 插件 运行组件测试时,该插件由 Vitest 提供支持,它可以生成覆盖率报告。结果在测试模块中进行汇总,显示您的已测试 stories 覆盖的语句百分比。

Screenshot of test module, expanded, showing coverage result

设置

覆盖率已包含在 Test 插件中,启用后,在为您的项目运行组件测试时将进行计算。要启用覆盖率,请按测试模块中的编辑按钮(铅笔图标)并将覆盖率切换为开启

Screenshot of test module, expanded, showing coverage toggle

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

# 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 配置中

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/**',
       ], 
    }
  }
})

用法

由于覆盖率已内置到 Test 插件中,因此您可以在运行测试的任何地方使用它。

Storybook UI

当您在 Storybook UI 中启用覆盖率时,覆盖率报告将在您运行测试后生成并在测试模块中进行汇总。您可以看到您的已测试 stories 覆盖的语句百分比,以及覆盖率是否满足 水印线

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

重要的是要理解,Storybook UI 中报告的覆盖率有三个重要的限制

  1. 覆盖率是使用您编写的 stories 计算的,而不是整个代码库。换句话说,它不会包含任何其他 Vitest 测试。
  2. 覆盖率只能针对项目中的所有 stories 进行计算,而不能针对单个 story 或一组 stories。
  3. 在激活监听模式时,不会计算覆盖率。启用覆盖率后,启用监听模式将禁用覆盖率。

CLI

与 Storybook Test 的其余部分一样,覆盖率构建于 Vitest 之上。这意味着您可以使用 Vitest CLI 生成覆盖率报告。

假设您使用如下所示的包脚本运行测试

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

那么您可以使用以下命令生成覆盖率报告

npm run test-storybook -- --coverage

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

上面的命令将仅计算您编写的 stories 的覆盖率,而不是整个代码库的覆盖率。

由于在考虑项目中的所有测试时,覆盖率最准确,因此您还可以使用以下命令为项目中的所有测试运行覆盖率

npx vitest --coverage

编辑器扩展

覆盖率也可通过 Vitest 的 IDE 集成获得。您可以直接在编辑器中计算和显示覆盖率结果。

Screenshot of test coverage in VSCode

请注意,此覆盖率将包括项目中的所有测试,而不仅仅是您编写的 stories。

CI

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

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

.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 中测试的更多信息,请参阅 Test 插件文档

配置

覆盖率提供程序

您可以通过在 Vitest 配置中设置 coverage.provider 选项来选择使用哪个提供程序,v8(默认)或 Istanbul,用于覆盖率计算

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

水印线

覆盖率提供程序都支持 水印线,即覆盖率的阈值。低水印线是通过测试所需的最低覆盖率,而高水印线是被认为良好的最低覆盖率。介于低水印线和高水印线之间的覆盖率百分比将被认为是可接受的,但不是理想的。

在测试模块中,覆盖率摘要将显示您的已测试 stories 覆盖的语句百分比,以及覆盖率是否满足水印线。低于低水印线,图标将显示为红色;介于低水印线和高水印线之间,图标将显示为橙色;高于高水印线,图标将显示为绿色。

Screenshot of test module, expanded, showing coverage result

要配置水印线,您可以调整 Vitest 配置

vitest.config.ts
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

CLI 的 add 命令会自动执行插件的安装和设置。要手动安装,请参阅我们关于如何安装插件的文档

使用以下命令启动您的 Storybook

npm run storybook

最后,打开一个新的终端窗口并使用以下命令运行测试运行器

npm run test-storybook -- --coverage

Coverage test output

配置

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

.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 选项
选项描述类型
checkProd配置插件以跳过生产环境中的检测
options: { istanbul: { checkProd: true,}}
boolean
cwd配置覆盖率测试的工作目录。
默认为 process.cwd()
options: { istanbul: { cwd: process.cwd(),}}
string
cypressVITE_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.jsonnyc.config.js

.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 选项,以允许插件以牺牲较慢的构建速度为代价来运行测试。

.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;

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

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

了解其他 UI 测试