
使用 Storybook 进行交互测试
你编写故事,Storybook 运行测试

Storybook 是构建组件驱动用户界面的行业标准工具。它允许你将组件的使用案例捕获为故事,并在浏览器中渲染它们。在开发过程中,你可以快速切换它们以验证 UI 外观。
但组件不仅仅是外观。Target、Adobe 和 Shopify 的团队在他们的测试中导入故事来验证组件行为。现在,这个工作流程已直接内置到 Storybook 中!
我很高兴宣布 Storybook 交互测试的 Beta 版本!你可以在故事本身中编写交互脚本并检查预期结果。
交互测试结合了 play 函数(在 Storybook 6.4 中引入)与新的测试运行器和断言库的功能。这些测试在实时浏览器中运行,可以通过命令行或你的 CI 服务器执行。
- ✅ 在开发时测试交互
- 🐛 在实时浏览器中交互式调试测试
- ⚡️ 通过命令行并行运行测试
- 🎭 由 Jest、Playwright 和 Testing Library 提供支持
- 👀 观察模式、过滤器以及你所期望的人体工程学特性
- 🛠 可定制的 API,具有完全可配置的“弹出”模式
宏观视角
自动化测试是严肃软件项目的基本要求。像 Jest 这样的现代测试工具提供了便捷的方式来针对代码库编写和运行大型单元测试套件。
测试组件的第一步是将它与外部关注点隔离开来,并提供模拟数据以在给定状态下渲染它。这正是你使用故事所做的。
团队已经编写了数千个故事,所以去年我们让将它们导入 Jest 变得很容易。开发者纷纷采用这种测试模式,因为他们可以将故事作为测试用例复用,并简化设置。这个工作流程非常棒,我们打算继续开发和支持它。

如果你可以直接在 Storybook 中运行 UI 测试呢?
Jest 和类似的工具非常适合运行通用测试。但这些测试是在 Node 中使用 JSDOM 运行的。这意味着你必须通过检查 HTML 块来调试失败的测试。你最终会花更多的时间查看 DOM 输出,而不是用户实际看到的组件。
此外,将代码分散在两个文件(故事文件中的测试用例以及测试文件中的操作和断言)中意味着更多的活动部件。
Storybook 交互测试通过允许你在故事文件本身中编写测试来简化你的工作流程。测试在浏览器中执行,并且你获得一个 GUI 来可视化和调试它们。

用于 UI 测试的 Storybook
交互式故事是 Storybook 中 UI 测试的第一步。它使得在 Storybook 中模拟用户交互成为可能,例如点击、拖动、轻触、输入等。用户可以向他们的故事添加一个 play function,该函数在故事渲染后运行。
使用 play function 编写交互和断言脚本
使用 play function,你可以在故事旁边编写测试。交互使用 Storybook 增强版的 Testing Library 编写。现在你可以使用 Jest 添加断言。例如
// AccountForm.stories.js
import { within, userEvent } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { AccountForm } from './AccountForm';
export default { component: AccountForm };
export const VerificationSuccess = {
args: { passwordVerification: true },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.type(canvas.getByTestId('email'), 'michael@chromatic.com');
await userEvent.type(canvas.getByTestId('password1'), 'k32904n£#1kjad');
await userEvent.type(canvas.getByTestId('password2'), 'k32904n£#1kjad');
await userEvent.click(canvas.getByTestId('submit'));
await expect(canvas.getByText('Everything is perfect. Your account is ready and we should probably get you started!')).toBeInTheDocument();
},
};
使用回放控件调试
The Storybook Interactions 插件帮助你在浏览器中可视化和调试这些测试。你可以使用回放控件逐步执行交互。如果测试失败,你可以使用浏览器开发工具检查 UI。能够查看和检查 UI 使得调试变得轻而易举。

play function 和 interactions 插件使得在同一工具中构建和测试组件成为可能。
使用测试运行器执行测试
测试运行器是故事的最后一章。它是一个实用程序,可以在你的整个 Storybook 中运行 play functions 并捕获有问题的故事。
错误是不可避免的。通常,一个组件的更改可能会无意中破坏其他组件。每次进行更改时,手动检查整个 Storybook 是不现实的。测试运行器验证故事是否正确渲染(即使是那些没有 play function 的故事)以及断言是否通过。

点击调试
测试运行器的一个关键工作流程是点击调试,它允许你在 Storybook 中浏览失败的测试。
当基于文本的测试工具在 CI 中运行 UI 测试失败时,解析产生的海量文本以找出问题所在是非常痛苦的。
像 Cypress 这样的端到端测试工具通过记录错误发生时的视频进一步改进了这一点。虽然这比基于文本的调试有所改进,但它无法替代能够检查你的实际 UI 来准确查看问题所在。我们正在使用 Storybook 实现浏览器内调试工作流程!
考虑以下在 CI 中失败的测试

点击链接会将你带到你已发布的 Storybook 中的失败故事,并选中 interactions 插件。你甚至可以单步调试失败的故事以找到根本原因(如果使用 Storybook 6.5,你需要启用该功能)。

交互测试示例
让我们以示例 Taskbox 应用为例,逐步了解设置和测试工作流程。它显示用户可以置顶、归档和编辑的任务列表。

首先安装测试运行器和相关软件包。它是零配置的,需要 Storybook 6.4 或更高版本。
npm install -D @storybook/addon-interactions @storybook/testing-library @storybook/jest jest @storybook/test-runner
然后将测试任务添加到项目的 package.json
中
{
"scripts": {
"test-storybook": "test-storybook"
}
}
(可选)在 interactions 插件中启用回放控件
// .storybook/main.js
module.exports = {
features: {
interactionsDebugger: true,
},
};
然后启动你的 Storybook
npm run storybook
最后,启动测试运行器
npm run test-storybook
测试运行器将所有故事视为测试。对于没有 play function 的故事,它验证故事是否渲染时没有错误。对于有 play function 的故事,它还会检查 play function 中的错误以及所有断言是否通过。

让我们添加一个测试来验证用户可以置顶一个任务。
// InboxScreen.stories.js
import React from 'react';
import { rest } from 'msw';
import { within, fireEvent, findByRole } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { InboxScreen } from './InboxScreen';
import { mockTasks } from './mocks/tasks';
export default {
component: InboxScreen,
title: 'InboxScreen',
};
const Template = (args) => <InboxScreen {...args} />;
export const Default = Template.bind({});
Default.parameters = {
msw: {
handlers: [
rest.get('/tasks', (req, res, ctx) => {
return res(ctx.json(mockTasks));
}),
],
},
};
export const PinTask = Template.bind({});
PinTask.parameters = { ...Default.parameters };
PinTask.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const getTask = (name) => canvas.findByRole('listitem', { name });
const itemToPin = await getTask('Export logo');
const pinButton = await findByRole(itemToPin, 'button', { name: 'pin' });
await fireEvent.click(pinButton);
const unpinButton = within(itemToPin).getByRole('button', { name: 'unpin' });
await expect(unpinButton).toBeInTheDocument();
};
每当你运行 test-storybook
时,测试运行器都会执行此测试。你可以在 interactions 面板中看到它并逐步执行每个设置步骤

有关在 play functions 中编写测试的更多示例和文档,请参阅 Storybook 官方文档。
熟悉的 Jest 人体工程学
我们希望测试运行器具有与 Jest 相同的高质量人体工程学特性,例如观察模式、测试过滤等。我们没有尝试自己重新创建所有这些功能,而是在 Jest 的基础上构建了 Storybook 测试运行器。
如果你已经使用 Jest,那么使用测试运行器应该很容易。例如,我们对过滤和观察模式使用相同的 CLI 选项
# filtered by stories matching "button"
npm run test-storybook button
# watch mode
npm run test-storybook --watch
当你在观察模式下运行测试运行器时,组件或故事的更改会触发测试的重新运行,正如你所期望的那样。

跨浏览器测试
我们还希望测试能在你用来查看故事和最终生产网站的相同浏览器中运行。为此,我们使用了 Playwright,它是 Puppeteer 的跨浏览器继任者。这些测试并行运行,性能与保真度较低的 JSDOM 测试相当。
# cross-browser testing
npm run test-storybook --browsers chromium firefox webkit

最后,我们希望能够轻松地对任何导出 stories.json
文件(从 6.4 开始选择加入)的生产 Storybook 运行测试运行器。
# remote testing
npm run test-storybook --url <http://my-storybook.com>
有关更详细的文档,包括 CLI 选项、API 文档和配置选项(包括持续集成设置),请参阅包的 README。
使用测试钩子 API 扩展和定制
我们还包含了一个实验性 API,用于在测试渲染生命周期中执行代码以定制其行为。例如,捕获故事的屏幕截图并运行快照测试。
// .storybook/test-runner.js
const { toMatchImageSnapshot } = require('jest-image-snapshot');
const customSnapshotsDir = `${process.cwd()}/__snapshots__`;
module.exports = {
setup() {
expect.extend({ toMatchImageSnapshot });
},
async postRender(page, context) {
const image = await page.screenshot();
expect(image).toMatchImageSnapshot({
customSnapshotsDir,
customSnapshotIdentifier: context.id,
});
},
};
这提供了 Storyshots 的替代方案,Storyshots 是我们最受欢迎的测试插件。与 Storyshots 不同的是,你还可以并行、在浏览器中以及在观察模式下运行这些测试。我们将在 Storybook 文档中添加更多关于如何使用测试运行器进行日常开发任务的指南。
加入 Beta 测试
Storybook 交互测试是我们对组件测试应有的愿景:快速、交互式,并与你已使用的工具集成。它结合了实时浏览器的直观调试环境与无头浏览器的性能和可脚本性。
交互测试目前处于 Beta 阶段 – 我们非常希望你尝试并报告任何问题。加入我们的 Storybook Discord 频道 #testing
,与我们一起踏上这段旅程。
新的测试运行器由 Yann Braga 和 Michael Shilman (我!) 开发,并得到了 Zoltan Olah, Tom Coleman, Ian VanSchooten 以及 Storybook 社区的反馈。接下来,我们将添加关于如何使用 play function、interactions 插件和测试运行器构建可靠 UI 的官方文档。
如果 Storybook 让你的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。你可以贡献新功能、修复错误或改进文档。加入我们的 Discord 或直接在 GitHub 上参与。要获取项目更新和早期功能访问权,请在下方注册 Storybook 的邮件列表。
在你的故事中编写交互测试。现在处于 Beta 阶段!
— Storybook (@storybookjs) 2022年2月24日
✅ 测试用户行为并自动化通过 CLI 运行
🐛 在浏览器中交互式调试
⚡️ 并行运行测试并跨浏览器执行
🎭 由 Jest、Playwright 和 Testing Library 提供支持
🧪 无闪烁https://#/MvfdTuNUio pic.twitter.com/Rs4qRIRJ8W