
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 blob 来调试损坏的测试。您最终花费更多时间查看 DOM 输出,而不是用户看到的实际组件。
此外,将代码拆分到两个文件(故事文件中的测试用例和测试文件中的操作和断言)意味着更多的移动部件。
Storybook 交互测试通过允许您在故事文件本身中编写测试来简化您的工作流程。测试在浏览器中执行,您将获得一个 GUI 来可视化和调试它们。

用于 UI 测试的 Storybook
交互式故事是在 Storybook 中测试 UI 的第一步。它使在 Storybook 中对用户交互(点击、拖动、点击、键入等)进行建模成为可能。用户可以向他们的故事添加一个 play 函数,该函数在故事渲染后运行。
使用 play 函数编写交i互和断言脚本
使用 play 函数,您可以与故事一起编写测试。交互是使用 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();
},
};
使用 播放控件 进行调试
Storybook Interactions 插件 帮助您在浏览器中可视化和调试这些测试。您将获得播放控件来逐步执行交互。如果测试失败,您可以使用浏览器开发人员工具检查 UI。能够看到和检查 UI 使调试变得轻而易举。

play 函数和 interactions 插件使得在同一工具中构建和测试组件成为可能。
使用测试运行器执行测试
测试运行器是故事的最后一章。它是一个实用程序,可以跨整个 Storybook 运行 play 函数并捕获损坏的故事。
Bug 是不可避免的。通常,一个组件中的更改可能会意外地破坏其他组件。每当您进行更改时,手动浏览整个 Storybook 是不现实的。测试运行器验证故事是否正确渲染(即使是没有 play 函数的故事),以及断言是否通过。

点击调试
测试运行器的关键工作流程之一是点击调试,它允许您在 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 函数的故事,它会验证故事是否在没有任何错误的情况下渲染。对于那些有 play 函数的故事,它还会检查 play 函数中是否有错误以及所有断言是否通过。

让我们添加一个测试来验证用户可以固定任务。
// 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 函数中编写测试的更多示例和文档,请参阅 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
文件的任何生产 Storybook 运行测试运行器(自 6.4 起 选择加入)
# 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 不同,您还可以在浏览器中和在观察模式下并行运行这些测试。我们将在 Storybook 文档中添加更多关于使用测试运行器进行日常开发任务的指南。
加入 Beta 版
Storybook 交互测试是我们对组件测试应有的愿景:快速、交互式且与您已使用的工具集成。它将实时浏览器的直观调试环境与无头浏览器的性能和可脚本性相结合。
交互测试目前处于 Beta 阶段 – 我们希望您试用它并报告任何问题。加入我们的 Storybook Discord #testing
频道,一起踏上这段旅程。
新的测试运行器由 Yann Braga 和 Michael Shilman (我!) 开发,并得到了 Zoltan Olah、Tom Coleman、Ian VanSchooten 和 Storybook 社区的反馈。接下来,我们将添加关于如何使用 play 函数、interactions 插件和测试运行器来构建万无一失的 UI 的官方文档。
如果 Storybook 使您的 UI 开发工作流程更轻松,请帮助 Storybook 变得更好。您可以贡献新功能、修复错误或改进文档。在 Discord 上加入我们,或直接在 GitHub 上参与。要获取项目更新和抢先体验功能,请注册 Storybook 的邮件列表,如下所示。
在您的故事中编写交互测试。现在是 Beta 版!
— Storybook (@storybookjs) February 24, 2022
✅ 测试用户行为并通过 CLI 自动化运行
🐛 在浏览器中交互式调试
⚡️ 并行且跨浏览器运行测试
🎭 由 Jest、Playwright 和 Testing Library 提供支持
🧪 无抖动https://#/MvfdTuNUio pic.twitter.com/Rs4qRIRJ8W