如何使用 Storybook 测试 UI
Storybook 的 故事 (stories) 是您的 UI 组件在各种状态和配置下的测试用例。使用 Storybook,您可以同时以多种方式开发和测试组件,并获得即时反馈。
Storybook 从整体角度解决了 UI 组件测试的问题,确保您不仅能快速可靠地执行组件测试,而且拥有完善的模式来在整个开发生命周期中开发、调试、维护甚至协作进行这些测试。
开始使用
如果您的项目正在使用 Vite,您很可能可以使用 Vitest 插件 在 Storybook 中运行您的组件测试。该插件构建在 Vitest 之上,Vitest 是一个快速轻量级的测试运行器,可与 Vite 无缝协作。
运行此命令,它将安装并配置 Vitest 插件和 Vitest
npx storybook add @storybook/addon-vitest完整的安装说明,包括项目要求,可在 Vitest 插件文档 中找到。
设置好项目后,您会在侧边栏底部看到一个测试小组件。运行测试后,您还会在侧边栏项目上看到测试状态指示器。此外,许多测试可以通过插件面板进行调试。

如果您无法在项目中将 Vitest 插件,仍然可以使用 test-runner 运行测试。
接下来,我们将介绍 Storybook 中测试的一些关键概念。
关键概念
Storybook 中的测试与其他测试环境类似。您一直在使用的绝大多数知识和技术在这里也适用。但也有一些显著的改进。
组件测试
组件测试是一个测试,它
- 在浏览器中渲染组件以实现高保真度
- 模拟用户与实际 UI 的交互,例如端到端 (E2E) 测试
- 仅测试 UI 的一个单元(例如,单个组件),**并且**可以深入到实现以模拟(mock)事物或操作数据,这类似于单元测试
这种结合使用真实浏览器、模拟行为和模拟(mocking)提供了强大的测试 UI 功能的方法。
在 Storybook 中,整个测试体验都围绕着组件测试构建。这意味着您可以在与故事相同的环境中,使用相同的工具和技术来运行您的测试。
Storybook 测试
Storybook Test 通过 Vitest 插件 实现对您的故事进行实时测试。它使用 Vitest 插件自动将您的故事转换为真正的 Vitest 测试,然后通过 Vitest 的浏览器模式运行。
观察模式
通过观察模式,在开发过程中获得即时测试反馈。它会监视您的代码——无论是组件源文件还是测试文件——的变化,并自动仅重新运行相关的测试。这非常适合测试驱动开发,即您先编写测试,然后再编写组件。
要激活观察模式,请点击测试小组件中的观察模式按钮(眼睛图标)

CI
如果您没有将 Storybook Test 作为 CI 的一部分运行,您将错过它提供的最大优势:在合并 PR 之前就能发现 Bug。
如果您已经在 CI 中运行 vitest,那么当您将更改提交到 Git 时,您的故事应该会自动“免费”运行为测试。
如果您尚未在 CI 中运行 Vitest,您应该进行设置。首先,在您的 package.json 中添加一个新的脚本
{
"scripts": {
"test-storybook": "vitest --project=storybook"
}
} 请注意,这假设您有一个名为“storybook”的 Vitest 项目来处理您的故事,这是安装 Storybook Test 时的默认配置。如果您重命名了它,请相应地调整脚本。
接下来,添加一个新的 CI 工作流。
如果您使用 Github Actions,它看起来会像这样
name: Storybook Tests
on: [push]
jobs:
test:
runs-on: ubuntu-latest
container:
# Make sure to grab the latest version of the Playwright image
# https://playwright.net.cn/docs/docker#pull-the-image
image: mcr.microsoft.com/playwright:v1.52.0-noble
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22.12.0
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test-storybook如果您使用的是其他 CI 提供商,请参阅我们的完整 CI 指南 以获取更多信息。
Storybook Test 默认使用 Playwright 渲染您的故事。为了获得最快的体验,您应该使用 已预装 Playwright 的机器镜像(如上面的代码片段所示)。
覆盖率
代码覆盖率可以了解哪些代码被测试了,哪些没有。它在您开发过程中充当安全网,帮助确保您的工作得到适当的测试覆盖。
在 Storybook Test 中,您可以通过两种方式查看您的故事提供的覆盖率。首先,测试小组件中会显示摘要。其次,点击该覆盖率摘要将打开一个完整的交互式报告。

在报告中,点击进入特定组件会显示被覆盖或未被覆盖的确切代码分支

每个项目的覆盖率报告看起来都会不同,但需要注意的关键点是
- 整体行/分支覆盖率,这可以作为高级健康检查。
- 未覆盖的具体行/分支,这些是潜在的测试漏洞。
在我们看来,目标不是达到 100% 的覆盖率并填补所有空白,而是提供一个晴雨表来帮助您判断代码/测试更改,并让空白告知您未测试的关键状态或交互。例如,如果您正在构建一个原型,您可能会完全跳过测试,而在关键应用程序中,您可能希望滴水不漏。
运行覆盖率分析可能会减慢您的测试运行速度,因此默认情况下它是关闭的。要激活覆盖率,请选中测试小组件中的覆盖率复选框。

CI 中的覆盖率
而且,既然我们正在查看覆盖率,请更新您的 CI 工作流以包含它
- yarn test
+ yarn test --coverage为什么我们要运行所有测试(yarn test)而不是只运行 Storybook 测试(yarn test-storybook)?因为覆盖率报告在考虑项目中的所有测试(而不仅仅是您编写的故事)时最准确。
查看 Storybook 特定的覆盖率 很有帮助,但在 CI 输出中,您希望看到项目的全面覆盖率。
这样我们就可以在每个 PR 中跟踪覆盖率的变化。
以上是您在 Storybook 中测试所需掌握的关键概念。现在,让我们来看看您可以执行的不同类型的测试。
测试类型
Storybook Test 支持各种测试类型,以帮助您从多个维度验证您的工作。
渲染测试
测试 Storybook 中组件的最重要工具是能以各种状态渲染您组件的故事。
然而,您可能没有意识到,一个基本的故事也是一个 冒烟测试,我们称之为 **渲染测试**。当故事成功渲染时测试通过,当它出错时测试失败。
根据您组件的复杂性,您可以通过这种方式捕获许多关键的 UI 状态。
交互测试
渲染测试是一种非常基础的交互测试。要测试有状态的组件或验证交互行为,请为您的故事定义一个 play 函数
// Replace your-framework with the name of your framework (e.g. react-vite, vue3-vite, etc.)
import type { Meta, StoryObj } from '@storybook/your-framework';
import { expect } from 'storybook/test';
import { Dialog } from './Dialog';
const meta = {
component: Dialog,
} satisfies Meta<typeof Dialog>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Opens: Story = {
play: async ({ canvas, userEvent }) => {
// Click on a button and assert that a dialog appears
const button = canvas.getByRole('button', { text: 'Open Modal' });
await userEvent.click(button);
await expect(canvas.getByRole('dialog')).toBeInTheDocument();
},
};但是 play 函数也可以用于设置状态、创建间谍、模拟网络、模拟用户与组件的交互、断言输出等等。它们是测试的核心,是您在 Storybook 中其余测试之旅的基础。
这是一个更复杂的示例,其中包括通过 fn 工具进行 函数间谍和模拟。
// Replace your-framework with the name of your framework (e.g. react-vite, vue3-vite, etc.)
import type { Meta, StoryObj } from '@storybook/your-framework';
import { fn, expect } from 'storybook/test';
import { users } from '../mocks/users';
import { EventForm } from './EventForm';
const meta = {
component: EventForm,
} satisfies Meta<typeof EventForm>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Submits: Story = {
// Mock functions so we can manipulate and spy on them
args: {
getUsers: fn(),
onSubmit: fn(),
},
beforeEach: async ({ args }) => {
// Manipulate `getUsers` mock to return mocked value
args.getUsers.mockResolvedValue(users);
},
play: async ({ args, canvas, userEvent }) => {
const usersList = canvas.getAllByRole('listitem');
await expect(usersList).toHaveLength(4);
await expect(canvas.getAllByText('VIP')).toHaveLength(2);
const titleInput = await canvas.findByLabelText('Enter a title for your event');
await userEvent.type(titleInput, 'Holiday party');
const submitButton = canvas.getByRole('button', { text: 'Plan event' });
await userEvent.click(submitButton);
// Spy on `onSubmit` to verify that it is called correctly
await expect(args.onSubmit).toHaveBeenCalledWith({
name: 'Holiday party',
userCount: 4,
data: expect.anything(),
});
},
};有关如何使用 play 函数编写交互测试和模拟的更多信息,请参阅 交互测试指南。
可访问性测试
Storybook 的 可访问性 (A11y) 插件 会对您的故事运行一组自动化检查,以帮助确保您的组件能够被所有用户使用,无论其能力或所使用的技术如何。这意味着支持以下要求:键盘导航、屏幕阅读器支持、可用的颜色对比度等。
可访问性不仅是正确的做法,而且日益成为一项强制性要求。例如,《欧洲无障碍法案》计划于 2025 年 6 月成为法律。同样在美国,《美国残疾人法案》(ADA) 和 《康复法案》第 508 条 等法律适用于许多面向公众的服务。
要与组件测试一起激活可访问性检查,请选中测试小组件中的“可访问性”复选框。

激活后,您会在侧边栏中看到可访问性失败。

任何失败都可以在可访问性插件面板中进行调试。

视觉测试
视觉测试是测试您组件的最有效方式。只需单击一个按钮,您就可以截取 Storybook 中每个故事的快照,并将这些快照与基线进行比较——即上次已知的“良好”快照。这不仅允许您检查组件的外观,而且它们还能够检查很大一部分组件功能,**而无需编写或维护任何测试代码**!
Storybook 支持跨浏览器视觉测试,原生使用 Chromatic,这是一个由 Storybook 团队创建的云服务。当您启用视觉测试时,每个故事都会自动转换为一个测试。这样您就能直接在 Storybook 中获得 UI Bug 的即时反馈。

快照测试
在大多数情况下,其他测试类型可以以更少的精力提供更多的覆盖。但在某些情况下,将故事的渲染标记与已知基线进行比较会很有帮助。例如,它可以帮助识别触发渲染错误的标记更改。
在其他测试工具中重用故事
故事是为重用而设计的,因此您可以在流行的测试工具中将它们用作测试用例。
端到端
有时您需要测试一个完整的流程,包含完整的运行堆栈。在这些情况下,您仍然可以通过在 Playwright 或 Cypress 端到端 (E2E) 测试中导入您的故事来使用它们。
单元
如果您愿意,可以在传统的测试环境中重用您的 Storybook 故事,例如 Vitest 或 Jest。
更多测试资源
