返回博客

故事即是测试

组件所有行为的单一数据来源

loading
Varun Vachhar
@winkerVSbecks
最后更新

想象一下,在没有单元测试的情况下编写一个复杂的函数。你将不得不手动验证每种场景——一遍又一遍。非常麻烦。然而,大多数团队就是这样构建用户界面的。他们调整一些代码,然后尝试手动模拟每个用户界面状态来检查它。使用这种随意的方法很容易遗漏错误。

Storybook 允许你采用更有条理的方法来构建用户界面。你将组件的用例记录为故事(stories),然后在隔离环境中渲染这些故事。这些故事充当单元测试,但用于渲染的组件。你实际上可以在浏览器中验证渲染的输出。

Storybook 是组件的行业标准用户界面开发工作台。Netflix、Slack、Stripe 和全球数千个团队都在使用它,因为它使他们能够为用户界面采用测试驱动的方法。让我们看看如何做到这一点。

带有眼球的单元测试

软件总是在不断变化。你修复错误并发布新功能。但是,每次进行更改时都回去验证每个组件是不现实的。尤其是在你没有单元测试来协助你的情况下。

对比一下大多数团队编写非用户界面代码的方式。每个函数都有一套单元测试。它们在每次推送时执行,以捕获回归错误。它们还允许开发人员清楚地思考代码在不同场景中需要做什么。

然而,单元测试在用户界面方面失败了,因为它很难隔离组件,而且大多数测试工具不允许你(开发人员)直观地验证界面。

要测试一个函数,你可以使用像 Jest 这样的框架。它使你能够编写定义明确的测试用例,并在隔离环境中执行每个用例。值得注意的是,测试在干净的环境中运行,而不是在应用程序中运行。

如果我们尝试使用这种基础设施来测试组件,我们会遇到一个大问题。这些测试没有眼球!它们在 Node 中运行,可能使用 JSDOM。这意味着我们无法查看用户界面并验证其外观。为此,我们必须在真实的浏览器中单独渲染每个组件。

我们需要的是编码组件的贴纸表。
有没有注意到设计师如何设置 Figma 或 Sketch 文件?他们通常会组装一个符号网格。每个符号代表组件的一种状态。每当设计师进行更改时,他们都可以快速验证所有变体的正确性。

这就是 Storybook 为用户界面开发人员所做的事情。它对每个组件及其各种使用方式进行编目。组件在隔离环境中渲染,以简化开发和测试。

故事是可视化测试用例

一个测试由三件事组成:设置、操作和断言。让我们为 Storybook 分解这个过程

  1. 设置: 每个故事描述了组件的一个用例。你提供该状态所需的适当 props 和数据。
  2. 操作: Storybook 在浏览器中渲染此组件。
  3. 断言: 你直观地检查故事并手动测试交互。

在开发期间,你可以循环浏览故事以运行快速手动验证。你实际上是在为界面编写单元测试。可视化单元测试!

话虽如此,组件不仅仅是外观。你还需要验证其可访问性、底层逻辑以及它如何融入更大的系统。故事是自动化和其他形式测试的起点。

一次编写,处处测试

在我们开始之前,值得回顾一下组件需要测试的不同特性

  1. 视觉: 给定一组 props 或状态,组件是否正确渲染?
  2. 组合: 多个组件是否协同工作?
  3. 交互: 事件是否按预期处理?
  4. 可访问性: 用户界面是否可访问?
  5. 用户流程: 跨各种组件的复杂交互是否有效?

有不同的工具可以检查这些方面。如果你独立测试每个方面,你最终会一遍又一遍地复制组件状态。这使得设置和维护变得很麻烦。

*.stories 文件是组件如何使用的记录。它是组件状态的单一数据来源,这也是你在用户界面中想要测试的内容。它们使用基于 ES6 模块的互操作标准编写,称为 组件故事格式。每个故事都导出为一个 JavaScript 函数,使你能够与其他工具重用它们。

你可以将它们与 Testing Library 一起使用,以验证交互和底层逻辑。在 Chromatic 中进行视觉回归测试。或者使用 Axe 审核可访问性。或者使用 Cypress 测试用户流程。所有这些都由同一组故事驱动!

对于不仅仅是简单函数的组件呢?复杂组件依赖于上下文、数据、应用程序状态等等。一旦你在 Storybook 中配置了这些,你就可以在其他测试工具中重用该配置。

可移植的故事

Storybook 及其插件生态系统允许你模拟数据、状态甚至 API 响应。这使你能够在隔离环境中构建和测试复杂的连接组件。更重要的是,你在 Storybook 中进行的任何设置都可以移植到其他测试工具。

@storybook/testing-react@storybook/testing-vue 包提供了实用程序来提升包装你的故事的所有 providers、context 和 decorators。你为隔离组件所做的所有配置都可以在 Jest、Cypress 等中重用。这是一个示例

import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';
import * as stories from './Button.stories';

/**
 * Every component that is returned maps 1:1 with the stories.
 * But, they also contain all decorators from story, meta and global levels.
 */
const { Primary, Secondary } = composeStories(stories);

test('renders primary button with default args', () => {
  render(<Primary />);
  const buttonElement = screen.getByText(
    /Text coming from args in stories file!/i
  );
  expect(buttonElement).not.toBeNull();
});

test('renders primary button with override props', () => {
  // You can override props and they will get merged with values from the Story's args
  render(<Primary>Hello world</Primary>);
  const buttonElement = screen.getByText(/Hello world/i);
  expect(buttonElement).not.toBeNull();
});
Button.test.js

当你将测试用例编写为故事后,很容易在顶部添加任何形式的断言。

Storybook 专为用户界面测试而生

在没有单元测试的情况下开发组件不仅耗时更长,而且不可能捕获所有错误。通常很难模拟你想覆盖的所有测试用例。或者你可能会完全遗漏一种状态。Storybook 可以轻松地在隔离环境中渲染组件并探索其所有排列组合。使你能够为构建用户界面采用更有条理和测试驱动的方法。

一个 stories 文件是你组件所有重要用例的目录。它是一个可移植的工件,允许你使用其他测试工具(如 Testing Library、Jest 和 Axe)来验证交互、可访问性和应用程序逻辑。

故事是你组件的单一数据来源。它们允许你在开发期间快速检查外观。使用自动化捕获回归错误或检查功能质量。最后,为你的所有用户界面元素生成必要的文档

加入 Storybook 邮件列表

获取最新的新闻、更新和版本

6,730位开发者及更多

我们正在招聘!

加入 Storybook 和 Chromatic 背后的团队。构建被成千上万的开发者在生产环境中使用的工具。远程优先。

查看职位

热门文章

组件故事格式 3.0

抛弃样板代码,迎接脚本化交互!
loading
Michael Shilman

测试复合组件

防止微小更改演变成重大回归错误
loading
Varun Vachhar

Storybook 6.3

针对用户界面开发进行优化
loading
Michael Shilman
加入社区
6,730位开发者及更多
为什么为什么选择 Storybook组件驱动的用户界面
文档指南教程更新日志遥测
社区插件参与进来博客
案例展示探索项目组件词汇表
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI