Storybook Test Runner 按 Tags 过滤测试:include、exclude、skip 配置与 CLI 实战指南

Storybook Test Runner 按 Tags 过滤测试:include、exclude、skip 配置与 CLI 实战指南

【免费下载链接】storybook Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation 【免费下载链接】storybook 项目地址: https://gitcode.com/GitHub_Trending/st/storybook

Storybook Test Runner 默认会为每一个 story 生成并执行测试,但真实项目中常常存在"尚未就绪的组件""仅用于展示的布局页""与测试无关的 token 样式"等场景。本指南基于 Storybook 仓库中的官方文档 test-runner-tags-config.md 与其宿主章节 test-runner.mdx,系统讲解如何利用 Storybook 的 tags 机制,通过配置文件或 --includeTags--excludeTags--skipTags 三个 CLI 参数,精准控制"哪些 story 被测、哪些被跳过、哪些被彻底排除",并结合仓库源码说明其底层实现原理。

一、为什么需要按 Tags 过滤测试

Test Runner 将你的每一个 story 都转化为可执行的测试:

  • 没有 play function 的 story:验证其能否无错误地渲染;
  • 带有 play function 的 story:还会检查 play function 中的断言是否全部通过。

这些测试运行在真实浏览器中,可通过命令行或 CI 执行。默认情况下,每次运行都会测试所有 story。当组件库规模变大后,全量测试会带来几个痛点:

  • 某些 story 处于"草稿"状态,尚未准备好被测试;
  • 某些 story 只服务于文档演示(如布局、token 展示),测试它们没有意义;
  • 某些 story 依赖外部服务或环境变量,不适合在常规测试中执行。

Storybook 原本引入 tags 特性是为了生成自动文档,但它可以被进一步扩展:test-runner 使用与 Storybook 相同的 tags 配置体系(或等价的 CLI 标志)来按需运行测试。该 CLI 过滤能力仅在 test-runner 最新稳定版(0.15 及以上)中可用。

二、在 test-runner 配置文件中启用 tags 过滤

在项目根目录的 .storybook/test-runner.js(或 TypeScript 版本的 test-runner.ts)中增加 tags 字段,即可声明三组过滤规则。以下是官方配置的完整示例:

module.exports = {
  tags: {
    include: ['test-only', 'pages'],
    exclude: ['no-tests', 'tokens'],
    skip: ['skip-test', 'layout'],
  },
};

如果你使用 TypeScript 编写配置文件,可以引入官方类型 TestRunnerConfig 获得完整的类型提示:

import type { TestRunnerConfig } from '@storybook/test-runner';

const config: TestRunnerConfig = {
  tags: {
    include: ['test-only', 'pages'],
    exclude: ['no-tests', 'tokens'],
    skip: ['skip-test', 'layout'],
  },
};

export default config;

三个选项的含义

选项说明
exclude匹配到所提供 tags 的 story 不会被测试(彻底排除)
include只有匹配到所提供 tags 的 story 子集才会被测试
skip匹配到所提供 tags 的 story 会被跳过,并在测试结果中被标记出来

三、通过 CLI 标志覆盖配置(0.15+)

除了配置文件,test-runner 还暴露了三个对应的 CLI 标志,它们适用于不想改动配置文件、或希望在 CI 流水线的不同阶段动态切换过滤规则的场景:

CLI 选项说明示例命令
--includeTags定义仅测试匹配这些 tags 的 story 子集(实验性)test-storybook --includeTags="test-only, pages"
--excludeTags阻止匹配这些 tags 的 story 被测试(实验性)test-storybook --excludeTags="no-tests, tokens"
--skipTags配置 test-runner 跳过匹配这些 tags 的 story(实验性)test-storybook --skipTags="skip-test, layout"

重要规则:CLI 标志的优先级高于配置文件。当同时提供 CLI 标志与配置文件中的选项时,CLI 标志会生效并覆盖配置文件中的对应选项。这为"默认配置 + 按需覆盖"的 CI 工作流提供了灵活性。

CLI 执行示例

# 仅测试带有 test-only 或 pages 标签的 story
test-storybook --includeTags="test-only, pages"

# 排除带有 no-tests 或 tokens 标签的 story
test-storybook --excludeTags="no-tests, tokens"

# 跳过带有 skip-test 或 layout 标签的 story
test-storybook --skipTags="skip-test, layout"

四、在 CSF 中为 story 打标签

过滤规则生效的前提是 story 本身带有匹配的 tags。Tags 可以在 组件级(metastory 级 声明,官方文档明确指出:为组件的 story 应用 tags 应在组件级(meta)或 story 级完成,跨 story 导入 tags 在 Storybook 中不受支持,也不会按预期工作。

场景一:禁用(排除)某些 story 的测试

当你想让 test-runner 完全跳过某些尚未就绪或不相关的 story 时,为其打上自定义 tag,并在配置文件的 exclude 中声明(或运行时使用 --excludeTags)。以下为 React(CSF 3)示例:

import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from './MyComponent';

const meta = {
  component: MyComponent,
  //👇 为文件内所有 story 提供 no-tests 标签
  tags: ['no-tests'],
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const ExcludeStory: Story = {
  //👇 为单个 story 添加 no-tests 标签,以便在 test-runner 配置启用后将其排除出测试
  tags: ['no-tests'],
};

场景二:只运行特定子集的测试

当你想让 test-runner 对某一部分 story 执行测试时,为其打上自定义 tag,并在配置文件的 include 中声明(或使用 --includeTags):

import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from './MyComponent';

const meta = {
  component: MyComponent,
  //👇 为文件内所有 story 提供 test-only 标签
  tags: ['test-only'],
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const IncludeStory: Story = {
  //👇 为单个 story 添加 test-only 标签,使其在 test-runner 配置启用后被纳入测试
  tags: ['test-only'],
};

场景三:跳过(标记为临时禁用)某些 story

当你想让 test-runner 忽略某些 story,但又希望它们在测试结果中明确显示为"被跳过"(而不是彻底消失)时,为其打上自定义 tag,并在配置文件的 skip 中声明(或使用 --skipTags):

import type { Meta, StoryObj } from '@storybook/react';
import { MyComponent } from './MyComponent';

const meta = {
  component: MyComponent,
  //👇 为文件内所有 story 提供 skip-test 标签
  tags: ['skip-test'],
} satisfies Meta<typeof MyComponent>;

export default meta;
type Story = StoryObj<typeof meta>;

export const SkipStory: Story = {
  //👇 为单个 story 添加 skip-test 标签,使其在 test-runner 配置启用后被跳过
  tags: ['skip-test'],
};

官方文档还提供了 Angular、Vue、Web Components 等多个框架以及 CSF Next(🧪 实验性写法,通过 preview.meta() / meta.story() 声明) 的完整代码示例,分别见 my-component-exclude-tags.mdmy-component-include-tags.mdmy-component-skip-tags.md。三者的核心逻辑一致:tag 加在 meta 上作用于整组 story,加在单个 story 上仅作用于该 story。

五、include、exclude、skip 三者的区别

三个选项的语义容易混淆,总结如下:

  • exclude(排除):匹配的 story 不会出现在测试执行列表中,相当于"不测这个"。
  • include(包含):只有匹配的 story 才会被测试,相当于"只测这些";不匹配的 story 全部被排除在外。
  • skip(跳过):匹配的 story 会被 test-runner 忽略,并在测试结果中被标记出来,表明这些测试被临时禁用——这是它和 exclude 的关键差异。

实践建议:

  • 组件开发中常用 include:例如仅对带有 test-only 标签的 story 跑交互测试,加快本地反馈循环;
  • 发布前可用 exclude:将文档型、占位型 story 排除出回归测试;
  • skip 适合临时屏蔽:如某条故事依赖的接口暂时不可用,跳过并保留可见的标记,方便后续恢复。

六、源码级原理:tags 过滤在 Storybook 中如何生效

本仓库(Storybook 源码)中,tags 过滤在多个层面都有对应实现,可以作为理解其工作机制的依据。

1. 默认值与配置合并

code/addons/vitest/src/vitest-plugin/index.ts 中,Vitest 插件会合并用户传入的 tags 选项,并为 include 设置了默认值 Tag.TEST(即 test 标签):

tags: {
  include: options?.tags?.include ?? [Tag.TEST],
  exclude: options?.tags?.exclude ?? [],
  skip: options?.tags?.skip ?? [],
},

也就是说,即使你不做任何配置,Storybook 默认也会只测试带有 test 标签(或满足其他默认规则的)的 story;一旦你在 test-runner 配置中提供了 include/exclude/skip,则会覆盖这些默认值。

2. 运行时传递:tags 注入测试环境

同一文件的 L379-L381 显示,最终合并后的 tags 会通过环境变量注入测试运行环境,供 setup 文件消费:

__VITEST_INCLUDE_TAGS__: finalOptions.tags.include.join(','),
__VITEST_EXCLUDE_TAGS__: finalOptions.tags.exclude.join(','),
__VITEST_SKIP_TAGS__: finalOptions.tags.skip.join(','),

从源码结构可以推断,include/exclude 主要用于在测试生成阶段决定"哪些 story 被纳入测试文件",而 skip 则在单条 story 执行阶段生效。

3. skip 的执行逻辑:context.skip()

code/addons/vitest/src/vitest-plugin/test-utils.ts 中可以看到 skip 的具体落地实现——当组合出的 story 的 tags 命中 skipTags 中的任意一个时,直接调用 context.skip() 跳过该条测试:

if (composedStory === undefined || skipTags?.some((tag) => composedStory.tags.includes(tag))) {
  context.skip();
}

这解释了 skipexclude 的行为差异:skip 仍会创建测试用例,只是在执行时被标记为跳过(测试报告中可见);而 exclude 则是在更早的阶段(测试收集/生成阶段)就把匹配的 story 过滤掉。

七、常见陷阱与排查

官方文档在 test-runner.mdx 中记录了该功能最典型的一个坑:

按 tags 过滤后测试仍被错误执行:如果你在 includeexclude 列表中提供了相同的 tag,test-runner 会以 exclude 列表为准执行测试,并忽略 include 列表。因此务必保证 includeexclude 中的 tags 互不相同。

另一个需要留意的是版本前提:--includeTags--excludeTags--skipTags 属于实验性功能,仅在 test-runner 0.15 及以上的稳定版本中可用;在旧版本中请使用配置文件方式,并确认你安装的 test-runner 版本支持 tags 过滤。

八、完整工作流示例

综合以上内容,一个典型的"按 tags 管理测试"工作流如下:

  1. 在 CSF 中打标签:为组件 meta 或单个 story 添加自定义 tags(如 test-onlyno-testsskip-test);
  2. 在 test-runner 配置中声明规则:在 .storybook/test-runner.js 中配置 tags.include / tags.exclude / tags.skip
  3. 按需用 CLI 覆盖:在 CI 的不同阶段使用 --includeTags--excludeTags--skipTags 动态调整过滤范围;
  4. 检查测试结果:被 skip 的 story 会在结果中被标记,便于追踪临时禁用的测试,及时恢复。
# 本地开发:只测关键路径
test-storybook --includeTags="test-only, pages"

# 发布前:排除文档型与 token 展示 story 后全量回归
test-storybook --excludeTags="no-tests, tokens"

# 临时跳过某个 story 并保留可见标记
test-storybook --skipTags="skip-test, layout"

通过将 tags 过滤与 CI 结合,你可以把"全量测试、关键子集测试、发布前回归"等不同粒度的测试策略落到同一条测试管线上,既保证测试覆盖面,又避免无关 story 拖慢流水线。

【免费下载链接】storybook Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation 【免费下载链接】storybook 项目地址: https://gitcode.com/GitHub_Trending/st/storybook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值