
Storybook 覆盖率测试遇上--test优化构建build.test.disabledAddons让 storybook/addon-coverage 重新生效本篇指南聚焦 Storybook 官方文档《Test runner》中“覆盖率插件不支持优化构建The coverage addon doesnt support optimized builds”一节给出的典型修复方案。该方案的核心是一条main.js|ts配置代码片段即仓库中 docs/_snippets/storybook-coverage-addon-optimized-config.md用于解决当你用storybook build --test生成面向测试的性能优化构建后覆盖率插件不再对代码插桩、无法产出覆盖率数据的问题。读完本文你将理解--test构建的取舍逻辑、build.test.disabledAddons的精确语义与底层过滤实现并能在 CSF 3 / CSF Next、React / Vue / Angular / Web Components 等多种技术栈下直接落地这一配置。问题缘起优化构建为什么会让覆盖率插件“失灵”Storybook 官方的 Test runner基于 Playwright 的故事级测试工具通常把测试跑在一个专门为测试优化过的生产构建上。该构建通过给storybook build传入--test标志生成目的是把对测试无意义、却拖慢构建与运行时速度的特性剔除掉。在 docs/writing-tests/integrations/test-runner.mdx 的 Troubleshooting 一节中官方明确描述了这一现象你为提升性能执行了带--test的生产构建同时你又依赖覆盖率插件storybook/addon-coverage对被测代码做 Istanbul 插桩以统计覆盖率结果会发现覆盖率插件根本没有对代码插桩——因为--test标志会移除对性能有影响的插件例如 Docs 与 coverage 插件本身解决办法修改.storybook/main.js|ts给出build.test.disabledAddons配置让 coverage 插件能继续工作——代价是构建会变慢。也就是说--test的“默认禁用名单”是一套覆盖了 Docs、coverage 等性能敏感插件的自动策略当业务确实需要在这些优化构建上测覆盖率时就必须显式地覆写这套名单。本节对应的文档原文说明了配置路径是.storybook/main.js|ts由根目录的 docs/api/main-config/main-config-build.mdx 统一定义其结构与类型。修复配置全貌一份可复制的.storybook/main.js|ts官方给出的修复片段即关联文档 docs/_snippets/storybook-coverage-addon-optimized-config.md在不同语法与框架下有多个等价变体。先看最通用的CSF 3写法JavaScript.storybook/main.jsCSF 3export default { // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. framework: storybook/your-framework, stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], addons: [storybook/addon-docs, storybook/addon-vitest, storybook/addon-coverage], build: { test: { disabledAddons: [storybook/addon-docs], }, }, };TypeScript.storybook/main.tsCSF 3// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. 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-docs, storybook/addon-vitest, storybook/addon-coverage], build: { test: { disabledAddons: [storybook/addon-docs], }, }, }; export default config;配置只改动了三处关键信息插件列表补上了storybook/addon-vitest与storybook/addon-coverage同时在build.test.disabledAddons中把storybook/addon-docs显式列入禁用名单。各技术栈与 CSF Next 的对应变体官方片段还提供了CSF Next试验性使用defineMain辅助函数的等价写法分别覆盖 React、Vue 3、Angular 与 Web Components。各变体与上面 CSF 3 版本的差异仅在于入口导入方式与framework取值addons与build.test部分完全一致渲染器defineMain的导入来源framework示例片段中的源码位置官方 TabReactReact/Vite、Next.js 等storybook/your-framework/node需替换为实际框架如 react-vite、nextjs、nextjs-vitereact-vite/nextjs等见原片段 CSF NextreactTabVue 3Vitestorybook/vue3-vite/nodevue3-vite原片段 CSF NextvueTabAngularstorybook/angular/nodeangular原片段 CSF NextangularTabWeb ComponentsVitestorybook/web-components-vite/nodeweb-components-vite原片段 CSF Nextweb-componentsTab例如Vue 3 Vite的完整写法是import { defineMain } from storybook/vue3-vite/node; export default defineMain({ framework: storybook/vue3-vite, stories: [../src/**/*.mdx, ../src/**/*.stories.(js|jsx|mjs|ts|tsx)], addons: [storybook/addon-docs, storybook/addon-vitest, storybook/addon-coverage], build: { test: { disabledAddons: [storybook/addon-docs], }, }, });而React CSF Next的片段当前以占位符形式给出源码注释写明需替换为实际框架导入语句为import { defineMain } from storybook/your-framework/node。对照本仓库源码可确认defineMain确实是各框架node入口统一导出的类型安全配置辅助函数——例如 Angular 框架导出位于 code/frameworks/angular/src/node/index.tsReact/Vite 框架导出位于 code/frameworks/react-vite/src/node/index.tsVue 3 的对应入口则在code/frameworks/vue3-vite下。逐项拆解这份配置到底做了什么理解这份配置的关键是看懂“同一个插件既出现在addons里、又出现在disabledAddons里”的用意addons数组中storybook/addon-docs、storybook/addon-vitest、storybook/addon-coverage三者同时注册保证普通构建文档站 / 开发模式下三者都可用当以--test生成优化构建时build.test段生效。disabledAddons: [storybook/addon-docs]显式把 Docs 从该构建中排除——Docs 属于“页面级”内容对跑覆盖率无益且最耗性能禁用它可以保住--test构建的大部分提速收益由于禁用名单里没有coverage 与 vitest这两个测试相关插件得以保留在构建中coverage 插件因此能继续对源码做 Istanbul 插桩测试结束后即可统计故事覆盖了哪些代码路径。framework字段需要替换为项目实际使用的 Storybook 框架包例如react-vite、nextjs、vue3-vite、angular等stories的 glob 描述的是组件示例与 MDX 文档文件的扫描范围。配套的执行命令覆盖率通常要结合测试命令一起使用。Test runner 场景下官方在 docs/writing-tests/integrations/test-runner.mdx 给出的命令行参数包括test-storybook --coverage让 Test runner 在跑完故事后统计覆盖率test-storybook --coverage --coverageDirectory coverage/ui/storybook额外把覆盖率报告输出到指定目录便于 CI 归档。如果走的是 Vitest 全家桶路线storybook/addon-vitest则可以参考 docs/writing-tests/in-ci.mdx为vitest命令追加--coverage标志或在 CI 配置里按需开启例如vitest --projectstorybook --coverage。本仓库中 Vitest 插件的覆盖率报告实现可见 code/addons/vitest/src/node/coverage-reporter.ts。build.test与disabledAddons官方配置项全解析build.test.disabledAddons只是build.test这一组测试专用构建优化开关中的一员。仓库文档 docs/api/main-config/main-config-build.mdx 将其完整类型定义为{ disableBlocks?: boolean; // 从构建中移除 storybook/addon-docs/blocksDocs Blocks 自动文档 disabledAddons?: string[]; // 在构建产物中被禁用的插件名单 disableMDXEntries?: boolean; // 移除用户手写的 MDX 文档入口 disableAutoDocs?: boolean; // 禁止 autodocs 自动文档进入构建 disableDocgen?: boolean; // 关闭 argType/组件属性的静态分析推断 disableSourcemaps?: boolean; // 覆盖默认的 sourcemap 生成行为 disableTreeShaking?: boolean; // 关闭 tree shaking }官方文档特别强调这些选项在storybook build传入--test时会被自动启用正常情况下不建议改动仅在“需要为某个项目禁用特定特性”或“排查构建问题”时才应覆写。针对本文主题核心是disabledAddons——官方语义为“设置一批会在构建产物中被禁用的插件”。具体的配套示例可见 docs/_snippets/main-config-test-disable-disableaddons.md。关于--test自动移除性能敏感插件如 Docs、coverage的行为描述可在 Test runner 文档的 docs/writing-tests/integrations/test-runner.mdx#L465-L469 找到原文依据。源码级原理disabledAddons的过滤是怎么实现的disabledAddons并非一个“在构建产物中跳过 bundle”的简单开关它在Storybook 预设preset加载阶段就会把对应插件从配置流中剔除。实现位于 code/core/src/common/presets.ts#L218-L228过滤逻辑只在名单非空、且当前 preset 不是“关键critical”preset 时生效匹配采用**子串包含name.includes(n)**规则插件/预设解析后的名字只要包含disabledAddons里的任意字符串即被过滤。例如禁用storybook/addon-docs时其派生的 blocks 等子预设也会一并被排除过滤同时作用于presets与addons两路输入然后再递归加载剩余部分。类型层面该字段定义在 code/core/src/types/modules/core-common.ts#L396TestBuildFlags.disabledAddons?: string[]与build配置结构的顶层类型TestBuildConfig呼应。仓库还为这一行为提供了完整的单元测试佐证见 code/core/src/common/presets.test.ts#L722-L754 中 “should filter out disabledAddons” 用例——它构造了一个包含storybook/addon-docs与addon-bar的addons列表并传入build.test.disabledAddons: [storybook/addon-docs]断言最终加载结果中addon-docs已被过滤掉而addon-bar等其他插件保留。这份测试与文档片段Docs 进disabledAddons、vitest/coverage 保留给出的行为完全一致可以作为你调整自定义禁用名单时的“行为参照”。在优化构建上跑覆盖率可行方案与限制综合官方文档docs/writing-tests/index.mdx与 Test runner 指南使用覆盖率时有几点需要明确默认关闭覆盖率分析会拖慢测试运行Storybook 默认关闭在 Test runner / Vitest 测试中需通过--coverage显式开启。报告形态Vite 与 Vitest 插件方案下覆盖率摘要会显示在测试组件面板中点击可打开完整的交互式报告CI 中则更关注“全项目综合覆盖率”把普通单测与故事测试一起统计而非仅故事的覆盖率。插桩机制与框架差异storybook/addon-coverage在 Webpack 下通过istanbul-lib-instrument插桩在 Vite 下通过vite-plugin-istanbul插桩基本做到零配置开箱即用其附加配置Istanbul 的include/exclude/extension/cwd/coverageVariable/cypress等选项见 docs/_snippets/storybook-coverage-addon-config-options.md 与 Test runner 文档中的参数表。特殊框架注意Vue 3、Svelte 等含专属单文件语法的框架需要把.vue/.svelte等扩展名加入 nyc/Istanbul 配置Test runner 文档的 docs/writing-tests/integrations/test-runner.mdx#L459-L463 提供了 Vue 示例不依赖 Webpack 加载器与 Vite 插件的框架例如以 Webpack 配置的 Angular插桩链路不完整需要额外的接入配置官方在 Test runner 文档中建议参考社区配方仓库。回到本文主题最需要记住的结论是--test优化构建默认会禁用包括 coverage 在内的性能敏感插件通过在build.test.disabledAddons中只保留“确实该在测试构建中消失的插件”如 Docs并让 coverage/vitest 留在addons中即可在优化构建上重新获得代码覆盖率能力——这是官方文档当前推荐的唯一标准做法代价是构建时间会有所回升。若你的项目在覆盖率与构建性能之间需要更细的取舍可以依据上文build.test全量选项表逐项开关如disableMDXEntries、disableAutoDocs等并在跑通后参照仓库的presets.test.ts行为用本地构建验证实际生效的插件集合。延伸阅读关联配置片段原文docs/_snippets/storybook-coverage-addon-optimized-config.md问题场景与官方说明docs/writing-tests/integrations/test-runner.mdx#L459-L473build.test全量选项docs/api/main-config/main-config-build.mdxdisabledAddons过滤实现与单元测试code/core/src/common/presets.ts#L218-L237、code/core/src/common/presets.test.ts#L722-L754、类型定义 code/core/src/types/modules/core-common.ts#L396Vitest 插件覆盖率报告实现code/addons/vitest/src/node/coverage-reporter.ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考