Storybook Test Runner 安装与实战指南:将每个 Story 变成可执行的自动化测试
导读
Storybook Test Runner 是 Storybook 官方提供的独立测试工具,它能将项目中的每一个 Story 自动转化为可执行的测试用例:没有 play function 的 Story 会验证其能否无错误渲染,带有 play function 的 Story 还会进一步校验交互逻辑与断言是否通过。这些测试运行在真实浏览器中(底层由 Jest 与 Playwright 驱动),既可在命令行本地执行,也可接入 CI 流水线。阅读本文后,你将掌握三种包管理器的安装方式、零配置启动流程、全部 CLI 参数、快照测试与代码覆盖率配置,以及面向 CI 的部署方案和高级 Hook 扩展能力。
一、安装 Test Runner:三个包管理器一条命令
Test Runner 是独立于 Storybook 的框架无关工具,需要额外安装。关联文档 docs/_snippets/test-runner-install.md 给出了官方推荐的三种包管理器安装命令,均以 --save-dev(开发依赖)方式写入 package.json:
# npm
npm install @storybook/test-runner --save-dev
# pnpm
pnpm add --save-dev @storybook/test-runner
# yarn
yarn add --dev @storybook/test-runner
安装前提
- 需要 Storybook 已正确初始化并可以本地启动(Test Runner 要求存在本地运行或已发布的 Storybook 实例才能执行测试);
- 建议在 Node.js 的 LTS 版本环境中运行;
- Test Runner 内部依赖 Playwright 浏览器二进制,首次运行测试前需确保 Playwright 的 chromium 浏览器可用,若 CI 环境中缺失可参照 Playwright 官方 CI 文档补充安装。
安装完成后,Test Runner 会自动注册 test-storybook 命令行可执行文件。从当前仓库的沙箱工程配置(如 code/sandbox/react-vite-default-ts/project.json 等各框架 sandbox 的 project.json)可以看到,Storybook 自身的集成测试体系正是通过 test-storybook 任务来运行的,这也印证了它是官方 CI 链路的标准一环。
二、零配置启动:三步跑通第一个测试
Test Runner 官方支持 Storybook 的零配置开箱即用,完整流程记录在 docs/writing-tests/integrations/test-runner.mdx 主文档中。
第 1 步:配置 npm script
在项目根目录的 package.json 中添加脚本:
{
"scripts": {
"test-storybook": "test-storybook"
}
}
第 2 步:启动 Storybook
npm run storybook
注意:Test Runner 要求要么有一个本地运行的 Storybook 实例,要么有一个已发布的 Storybook,否则无法运行现有测试。
第 3 步:新开终端运行测试
npm run test-storybook
默认情况下 Test Runner 假设本地 Storybook 运行在 6006 端口。执行后它会遍历所有 Story:无 play function 的 Story 校验渲染是否报错;有 play function 的 Story 额外执行 play 逻辑并检查断言结果。
自定义配置:eject 模式
零配置之外,你可以运行 test-storybook --eject 获得更细粒度的控制。它会在项目根目录生成一个 test-runner-jest.config.js 文件供你修改。由于 Test Runner 底层同时使用了 jest-playwright,你还可以在生成配置中提供 testEnvironmentOptions 进一步定制浏览器运行环境。
三、CLI 参数速查表
Test Runner 由 Jest 驱动,接受其 CLI 选项的子集(如 --watch、--maxWorkers),如果你项目里已经使用这些标志,可以无缝迁移。下表完整罗列了官方文档中的全部可用参数:
| 选项 | 说明 |
|---|---|
--help |
输出用法信息,如 test-storybook --help |
-s, --index-json |
以 index.json 模式运行(要求 Storybook 兼容,可自动检测),如 test-storybook --index-json |
--no-index-json |
禁用 index.json 模式,如 test-storybook --no-index-json |
-c, --config-dir [dir-name] |
指定加载 Storybook 配置的目录,如 test-storybook -c .storybook |
--watch |
监听模式运行 |
--watchAll |
监听文件变更并重跑所有测试 |
--coverage |
对 stories 和组件运行覆盖率测试,如 test-storybook --coverage |
--coverageDirectory |
指定覆盖率报告输出目录,如 test-storybook --coverage --coverageDirectory coverage/ui/storybook |
--url |
指定测试目标 URL,适合自定义 Storybook 地址,如 test-storybook --url http://the-storybook-url-here.com |
--browsers |
指定测试浏览器,可选 chromium、firefox、webkit 中的一个或多个,如 test-storybook --browsers firefox chromium |
--maxWorkers [amount] |
限制 worker 池最大并行数,如 test-storybook --maxWorkers=2 |
--testTimeout [amount] |
定义单个测试超时毫秒数,适合长耗时测试,如 test-storybook --testTimeout=60000 |
--no-cache |
禁用缓存 |
--clearCache |
清空 Jest 缓存目录后退出,不运行测试 |
--verbose |
按测试套件层级展示单个测试结果 |
-u, --updateSnapshot |
重新记录本次运行中失败的快照 |
--eject |
生成本地配置文件以覆盖默认值 |
--json |
以 JSON 格式输出测试结果(其余输出写入 stderr) |
--outputFile |
配合 --json 将结果写入文件,如 test-storybook --json --outputFile results.json |
--junit |
以 junit 文件格式上报测试信息 |
--ci |
CI 模式下不自动存储新快照,而是让测试失败并要求配合 --updateSnapshot |
--shard [index/count] |
要求 CI 环境,将测试套件拆分到多台机器并行,如 test-storybook --shard=1/8 |
--failOnConsole |
浏览器控制台出现错误即判定测试失败 |
--includeTags |
实验特性,只测试匹配 tags 的 stories,如 test-storybook --includeTags="test-only, pages" |
--excludeTags |
实验特性,排除匹配 tags 的 stories,如 test-storybook --excludeTags="no-tests, tokens" |
--skipTags |
实验特性,跳过匹配 tags 的 stories,如 test-storybook --skipTags="skip-test, layout" |
针对部署版 Storybook 运行测试
默认目标端口是本地 6006。若要测试已部署的 Storybook,有两种方式:
# 方式一:--url 参数
test-storybook --url http://the-storybook-url-here.com
# 方式二:TARGET_URL 环境变量
TARGET_URL=https://the-storybook-url-here.com yarn test-storybook
四、无障碍(a11y)测试
安装 a11y 插件 后,Test Runner 可以在交互测试的同时运行无障碍测试。仓库中的 code/addons/a11y 目录即该插件的完整实现,包含测试规则、面板组件与配置项。更详细的配置说明见 docs/writing-tests/accessibility-testing.mdx。
五、快照测试
快照测试 用于验证边界情况(如错误态)是否被正确处理,以及组件渲染输出在不同测试运行间是否保持一致。
启用快照测试
在 Storybook 目录(.storybook/)下新增配置文件,利用 postVisit Hook 捕获渲染结果:
module.exports = {
async postVisit(page, context) {
// #storybook-root 元素包裹着 story;在 Storybook 6.x 中该选择器为 #root
const elementHandler = await page.$('#storybook-root');
const innerHTML = await elementHandler.innerHTML();
expect(innerHTML).toMatchSnapshot();
},
};
完整示例见 docs/_snippets/test-runner-dom-snapshot-testing.md。运行 test-storybook 后,会为每个 story 在项目 __snapshots__ 目录生成对应的快照文件。
自定义快照目录
默认快照路径有固定的命名约定,如需变更可自定义 snapshotResolver:创建 snapshot-resolver.js 实现自定义解析器(参考 docs/_snippets/test-runner-config-snapshot-resolver.md),然后在 test-runner-jest.config.js 中启用 snapshotResolver 选项。
自定义快照序列化
默认使用 jest-serializer-html 序列化 HTML 快照。如果项目使用 Emotion、Angular 的 ng 属性等会生成基于 hash 的类名库,序列化可能不稳定。此时可创建 snapshot-serializer.js 自定义序列化器(参考 docs/_snippets/test-runner-custom-snapshot-serializer.md),并在 test-runner-jest.config.js 中通过 snapshotSerializers 启用。执行时 Test Runner 会先用正则将动态生成的属性替换为静态值,再生成快照,从而保证跨运行的一致性。
六、代码覆盖率
安装与运行
Storybook 提供了 @storybook/addon-coverage 相关的覆盖率插件(见官方文档说明),底层由 Istanbul 驱动,可为 JavaScript 生态中最常用的框架和构建器提供开箱即用的代码插桩。它与 Playwright 等现代测试工具协同工作,推荐搭配 Test Runner 使用。
npm install @storybook/addon-coverage --save-dev
启动 Storybook 后,在新终端运行:
test-storybook --coverage
覆盖率配置项
覆盖率插件对 Storybook 提供零配置支持:Webpack 下通过 istanbul-lib-instrument 插桩,Vite 下通过 vite-plugin-istanbul 插桩。可在 .storybook/main.js|ts 中通过 options.istanbul 传递更多参数。
Vite 选项:checkProd(生产环境跳过插桩,boolean)、cwd(工作目录,默认 process.cwd(),string)、cypress(用 CYPRESS_COVERAGE 替换 VITE_COVERAGE,boolean)、exclude / include(覆盖/包含的文件列表,Array<String> 或 string)、extension(扩展参与覆盖的文件后缀)、forceBuildInstrument(构建模式强制插桩,boolean)、nycrcPath(现有 nyc 配置文件相对路径,string)、requireEnv(覆盖 VITE_COVERAGE 环境变量值,boolean)。
Webpack 5 选项:autoWrap(支持顶层 return,boolean)、compact(压缩插桩代码,调试时可设 false)、coverageVariable(Istanbul 存储覆盖率结果的全局变量名,默认 __coverage__,string)、cwd、debug(调试日志,boolean)、esModules(支持 ES Module 语法,boolean)、exclude / include / extension / nycrcPath(同上)、preserveComments(保留注释,boolean)、produceSourceMap(生成 source map,boolean)、sourceMapUrlCallback(生成 source map 时的回调函数,function)。
与其它覆盖率报告工具集成
覆盖率测试与 Test Runner、coverage 插件配合开箱即用,同时也可接入其它报告工具(如 LCOV):利用生成的 coverage/storybook/coverage-storybook.json 输出自行生成报告。若在 Vue 3、Svelte 等含特殊文件的框架中运行覆盖率,需要在 nyc 配置文件(.nycrc.json 或 nyc.config.js)中启用对应文件扩展名。
七、CI 集成方案
方案一:对已部署的 Storybook 运行测试(GitHub Actions)
如果 Storybook 通过 Vercel 或 Netlify 发布,它们会在 GitHub Actions 中发出 deployment_status 事件。可以读取 deployment_status.target_url 作为 TARGET_URL 环境变量。该方案要求发布的 Storybook 可公开访问;若需鉴权,建议改用下面的本地构建方案。
方案二:对未部署的 Storybook 运行测试
使用 CI 提供方构建 Storybook 后运行测试。官方推荐借助 concurrently、http-server、wait-on 三个第三方库,先构建静态站点再执行 Test Runner。注意:Storybook 默认将构建产物输出到 storybook-static 目录,若自定义了构建目录需相应调整配置。
八、高级配置:Test Hook API
Test Runner 渲染 story 并执行其 play function。某些行为无法在浏览器内的 play function 中实现(例如截取视觉快照),需要通过 Node 侧执行。Test Runner 导出了可在全局覆写的测试 Hook,用于在 story 渲染之前和之后介入测试生命周期:
| Hook | 说明 |
|---|---|
prepare |
为测试准备浏览器,签名 async prepare({ page, browserContext, testRunnerConfig }) {} |
setup |
在全部测试运行前执行一次,setup() {} |
preVisit |
在 story 初次访问、渲染前执行,async preVisit(page, context) {} |
postVisit |
在 story 访问并完全渲染后执行,async postVisit(page, context) {} |
注意:除
setup外的 Hook 均为异步函数;preVisit与postVisit额外接收两个参数——Playwright 的 page 对象,以及包含 story 的id、title、name的 context 对象。这些 Hook 属于实验特性,可能发生破坏性变更,官方建议尽可能在 story 的 play function 内完成测试。
启用方式是在 Storybook 目录下新增 .storybook/test-runner.js(或 .ts)配置文件:
module.exports = {
// 在测试运行器开始运行测试前执行
setup() {
// 在这里添加你的配置
},
/* 在 story 初次访问、渲染前执行。
* page 参数是 story 的 Playwright page 对象。
* context 参数是包含 story 的 id、title、name 的 Storybook 对象。
*/
async preVisit(page, context) {
// 在这里添加你的配置
},
/* 在 story 访问并完全渲染后执行。参数同上。 */
async postVisit(page, context) {
// 在这里添加你的配置
},
};
TypeScript 版本可使用 TestRunnerConfig 类型获得类型提示,完整代码见 docs/_snippets/test-runner-hooks-example.md。测试执行的完整生命周期为:setup 先于所有测试执行 → 生成包含必要信息的 context 对象 → Playwright 导航到 story 页面 → 执行 preVisit → 渲染 story 并执行 play function → 执行 postVisit。
辅助函数(Helpers)
Test Runner 还导出两个辅助函数,用于访问 Storybook 内部信息:
getStoryContext(page, context):获取 story 的完整上下文(parameters、args、argTypes 等)。典型用法是在preVisit中读取 story 的parameters.viewport.defaultViewport,并据此设置 Playwright 页面的 viewport 尺寸(示例见 docs/_snippets/test-runner-custom-page-viewport.md)。waitForPageReady(page):等待页面完全加载(包括图片、字体等异步资源),专为图片快照测试设计(示例见 docs/_snippets/test-runner-helper-function.md)。
九、按标签过滤测试(实验特性)
默认情况下 Test Runner 会测试所有 story。通过 tags 配置可以精确控制测试范围。配置方式有两种:.storybook/test-runner.js 配置文件中的 tags 选项,或 CLI 的 --includeTags / --excludeTags / --skipTags 标志(需 0.15 及以上版本)。CLI 标志优先于配置文件并会覆盖后者。
| 选项 | 说明 |
|---|---|
exclude |
阻止匹配标签的 stories 被测试 |
include |
只测试匹配标签的 stories |
skip |
跳过匹配标签的 stories(在结果中标记为临时禁用) |
module.exports = {
tags: {
include: ['test-only', 'pages'],
exclude: ['no-tests', 'tokens'],
skip: ['skip-test', 'layout'],
},
};
完整示例见 docs/_snippets/test-runner-tags-config.md。三个典型场景:
- 禁用测试:为 story 添加自定义 tag 后通过
--excludeTags排除,适合排除尚未就绪或与测试无关的 story(示例见 docs/_snippets/my-component-exclude-tags.md); - 只测子集:用
--includeTags只运行特定 story(示例见 docs/_snippets/my-component-include-tags.md); - 跳过测试:用
--skipTags暂时禁用某部分测试(示例见 docs/_snippets/my-component-skip-tags.md)。
标签应在组件层级(
meta)或 story 层级添加,跨 story 导入标签不受支持。若include与exclude列表出现相同标签,Test Runner 会按exclude执行并忽略include,因此务必保持两列表的标签互不相同。
十、部署版 Storybook 的认证与 index.json 模式
为受保护的 Storybook 添加 HTTP 头
若托管平台需要认证,可在 .storybook/test-runner.js 中配置 getHttpHeaders 函数。该函数接收 fetch 调用与页面访问的 URL 作为输入,返回需要设置的请求头对象:
module.exports = {
getHttpHeaders: async (url) => {
const token = url.includes('prod') ? 'XYZ' : 'ABC';
return {
Authorization: `Bearer ${token}`,
};
},
};
示例见 docs/_snippets/test-runner-auth.md。这是因为 Test Runner 会通过 fetch 请求检查实例状态与 story 索引。
index.json 模式
测试本地 Storybook 时,Test Runner 将 story 文件转换为测试;测试远程 Storybook 时,则使用 index.json(即此前的 stories.json,是所有 story 的静态索引)。当本地与远程 Storybook 不同步或无法访问源码时,index.json 是测试目标最准确的表示。可通过 --index-json 强制开启,--no-index-json 关闭。注意 index.json 模式与 watch 模式不兼容。
检查方式:在浏览器中访问部署实例的 index.json 地址(如 https://your-storybook-url-here.com/index.json),若 JSON 以 "v": 3 键开头、紧随其后是包含 story ID 映射的 stories 键,则说明支持该模式。
十一、与 Chromatic 的区别
Test Runner 是通用的测试工具,可在本地或 CI 上运行,可配置、可扩展以运行各类测试。Chromatic 是基于云的视觉/交互测试服务,无需搭建 Test Runner,并与 git 提供方同步、管理私有项目的访问控制。两者也可组合使用:本地用 Test Runner、CI 用 Chromatic;或视觉与组件测试交给 Chromatic、自定义测试交给 Test Runner。
十二、常见故障排查
| 问题 | 解决方案 |
|---|---|
测试超时(Timeout - Async callback was not invoked within the 15000 ms timeout) |
故事数量过多或 CI 内存过低时,限制并行 worker 数:"test-storybook:ci": "yarn test-storybook --maxWorkers=2" |
| CLI 错误输出过短 | 默认截断 1000 字符,可通过 DEBUG_PRINT_LIMIT 调整,如 DEBUG_PRINT_LIMIT=5000 yarn test-storybook |
| 其它 CI 环境运行异常 | Test Runner 基于 Playwright,需按 Playwright CI 文档配置特定 docker 镜像等 |
Yarn PnP 项目报 PlaywrightError: jest-playwright-preset: Cannot find playwright package to use chromium |
将 nodeLinker 改为 node-modules,或直接安装 Playwright 依赖并执行浏览器安装命令 |
| coverage 插件不插桩优化构建产物 | --test 标志会移除影响性能的插件(如 Docs、coverage),需要在 `.storybook/main.js |
| coverage 插件不支持插桩代码 | 基于 Webpack5 loader 与 Vite 插件实现的框架(如 Angular 配置 Webpack)需要额外配置 |
结语与延伸阅读
至此,你已经掌握了从安装到 CI 落地的完整 Test Runner 使用链路。值得留意的是,官方文档指出 Test Runner 已逐步被 Vitest 插件 取代——后者基于更快的 Vitest 浏览器模式提供同等功能,并能直接在 Storybook 应用中运行交互、无障碍与视觉测试。若你的 Storybook 框架基于 Vite,官方建议优先选用 Vitest 插件。
更多测试主题可继续阅读:交互测试(docs/writing-tests/interaction-testing.mdx)、无障碍测试(docs/writing-tests/accessibility-testing.mdx)、视觉测试(docs/writing-tests/visual-testing.mdx)、快照测试(docs/writing-tests/snapshot-testing.mdx)、覆盖率(docs/writing-tests/test-coverage.mdx)、CI 集成(docs/writing-tests/in-ci.mdx)、端到端测试(docs/writing-tests/integrations/stories-in-end-to-end-tests.mdx)与单元测试(docs/writing-tests/integrations/stories-in-unit-tests.mdx)。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
