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

Storybook 是构建组件驱动的用户界面的行业标准工具。它使您能够将组件用例捕获为 story,并在浏览器中进行渲染。在开发过程中,您可以快速地在它们之间切换以验证 UI 外观。
但是,组件不仅仅关乎外观。Target、Adobe 和 Shopify 的团队会在测试中导入 story 来验证组件行为。现在,这个工作流程已内置到 Storybook 中!
我很高兴地宣布 Storybook 交互测试的 Beta 版本! 您可以在 story 中脚本化交互并检查预期结果。
交互测试结合了 play functions(在 Storybook 6.4 中引入)的力量与新的 测试运行器和断言库。这些测试在实时浏览器中运行,可以通过命令行或 CI 服务器执行。
- ✅ 在开发过程中测试交互
- 🐛 在实时浏览器中交互式调试测试
- ⚡️ 通过命令行并行运行测试
- 🎭 由 Jest、Playwright 和 Testing Library 提供支持
- 👀 监视模式、过滤器以及您期望的易用性
- 🛠 可自定义的 API,具有完全可配置的“eject”模式
总体而言
自动化测试是严肃软件项目的必备条件。像 Jest 这样的现代测试工具提供了便捷的方式来针对代码库编写和运行大型单元测试套件。
测试组件的第一步是将其与外部因素隔离开,并提供模拟数据以使其渲染到给定的状态。这正是您使用 story 所做的。
团队已经在编写成千上万个 story,所以去年我们使其易于 导入 Jest。开发人员蜂拥而至这种测试模式,因为他们可以重用 story 作为测试用例并简化设置。这个工作流程非常神奇,我们打算继续开发和支持它。

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

用于 UI 测试的 Storybook
交互式 story 是在 Storybook 中测试 UI 的第一步。它使得在 Storybook 中模拟用户交互(点击、拖动、轻触、输入等)成为可能。用户可以将 play function 添加到他们的 story 中,该函数在 story 渲染后运行。
使用 play function脚本化交互和断言
使用 play functions,您可以在 story 旁边编写测试。交互使用 Storybook instrumented 版本的 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();
},
};使用回放控件进行调试
Storybook Interactions addon 帮助您在浏览器中可视化和调试这些测试。您可以使用回放控件逐步执行交互。如果测试失败,您可以使用浏览器开发者工具检查 UI。能够查看和检查 UI 使调试变得轻而易举。

play function 和 interactions addon 使得在同一个工具中构建和测试组件成为可能。
使用测试运行器执行测试
Test runner 是故事的最后一个章节。它是一个实用工具,可以跨您的整个 Storybook 运行 play functions,并捕获损坏的 story。
bug 是不可避免的。通常一个组件中的更改可能会无意中破坏其他组件。每次更改时,手动遍历整个 Storybook 都是不现实的。测试运行器会验证 story 是否正确渲染(即使是没有 play function 的),并且断言是否通过。

点击调试
测试运行器的关键工作流程之一是“点击调试”,它允许您在 Storybook 中浏览失败的测试。
当基于文本的测试工具在 CI 中 UI 测试失败时,解析大量的文本来找出问题所在会非常痛苦。
像 Cypress 这样的端到端测试工具通过录制失败视频进一步改进了这一点。虽然这比基于文本的调试有所改进,但它不能替代检查实际 UI 以确切了解出了什么问题。我们正在通过 Storybook 实现浏览器内调试工作流程!
考虑以下 CI 中的失败测试

点击链接会将您带到您已发布的 Storybook 上的失败 story,并选中 interactions addon。您甚至可以逐步执行失败的 story 来找到根本原因(如果您使用的是 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 addon 中启用回放控件
// .storybook/main.js
module.exports = {
features: {
interactionsDebugger: true,
},
};
然后启动您的 Storybook
npm run storybook
最后,启动测试运行器
npm run test-storybook
测试运行器将所有 story 都视为测试。对于没有 play function 的 story,它会验证 story 是否没有错误地渲染。对于有 play function 的 story,它还会检查 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
当您在监视模式下运行测试运行时,您的组件或 story 的更改会按预期触发测试的重新运行。

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

最后,我们希望轻松地针对任何导出 stories.json 文件的生产 Storybook 运行测试运行器(自 6.4 版本起选择加入)。
# remote testing
npm run test-storybook --url <http://my-storybook.com>
有关更详细的文档,包括 CLI 选项、API 文档和配置选项(包括持续集成设置),请参阅包的README。
使用测试钩子 API 进行扩展和自定义
我们还包含了一个实验性的 API,用于在测试渲染生命周期中执行代码以自定义其行为。例如,捕获 story 的截图并运行快照测试。
// .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 不同,您还可以并行、在浏览器中以及在监视模式下运行这些测试。我们将在 Storybook 文档中添加更多关于使用测试运行器进行日常开发任务的示例。
加入 Beta
Storybook 交互测试是我们对组件测试应有的状态的愿景:快速、交互式且与您已使用的工具集成。它结合了实时浏览器的直观调试环境与无头浏览器的性能和可脚本性。
交互测试处于 Beta 阶段 – 我们希望您试用并报告任何问题。加入我们在 Storybook Discord 的 #testing 频道,一起踏上这段旅程。
新的测试运行器由 Yann Braga 和 Michael Shilman(我!) 开发,并得到了 Zoltan Olah、Tom Coleman、Ian VanSchooten 和 Storybook 社区的反馈。接下来,我们将添加关于如何使用 play function、interactions addon 和 test runner 构建强大 UI 的官方文档。
如果 Storybook 使您的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。您可以贡献新功能、修复 bug 或改进文档。加入我们的 Discord 或直接在 GitHub 上参与。要获取项目更新和早期功能访问权限,请在下方注册 Storybook 的邮件列表。
在您的 story 中编写交互测试。现已推出 Beta 版!
— Storybook (@storybookjs) 2022年2月24日
✅ 测试用户行为并通过 CLI 自动化运行
🐛 在您的浏览器中交互式调试
⚡️ 并行运行和跨浏览器运行测试
🎭 由 Jest、Playwright 和 Testing Library 提供支持
🧪 无闪烁https://#/MvfdTuNUio pic.twitter.com/Rs4qRIRJ8W