返回面向开发者的设计系统
React
章节
  • 简介
  • 架构
  • 构建
  • 评审
  • 测试
  • 文档
  • 分发
  • 工作流
  • 结论

通过测试维护质量

如何测试设计系统的外观、功能和可访问性
此社区翻译尚未更新至最新版本的 Storybook。请通过将英文指南中的更改应用于此翻译来帮助我们更新它。 欢迎提交 Pull Request.

在第 5 章中,我们自动化了设计系统测试以防止 UI 错误。本章深入探讨了 UI 组件的哪些特性需要测试以及要避免的潜在陷阱。我们研究了 Wave、BBC 和 Salesforce 的专业团队,以制定一个平衡全面覆盖、简单设置和低维护的测试策略。

UI 组件测试的基础知识

在我们开始之前,让我们弄清楚测试哪些内容是有意义的。设计系统由 UI 组件组成。每个 UI 组件都包含故事(排列组合),这些故事描述了给定一组输入(props)的预期外观和感觉。然后,故事由浏览器或设备为最终用户呈现。

Component states are combinatorial

哇!正如您所见,一个组件包含许多状态。将状态数乘以设计系统组件的数量,您就可以理解为什么跟踪所有这些状态是一项西西弗斯式的任务。实际上,手动审查每种体验是不可持续的,尤其是在设计系统不断增长的情况下。

因此,更有理由现在设置自动化测试,以便在未来节省工作。

准备测试

我在上一篇文章中调查了 4 个前端团队关于专业 Storybook 工作流程的信息。他们就编写故事的最佳实践达成了一致,以使测试变得简单而全面。

将支持的组件状态表述为故事,以明确哪些输入组合会产生给定的状态。无情地省略不支持的状态以减少噪音。

一致地渲染组件,以减轻可能由随机化 (Math.random()) 或相对 (Date.now()) 输入触发的可变性。

“最好的故事类型让您能够可视化组件在实际应用中可能经历的所有状态”——Tim Hingston,Apollo GraphQL 技术主管

可视化测试外观

设计系统包含展示型 UI 组件,这些组件本质上是可视化的。可视化测试验证渲染 UI 的视觉方面。

可视化测试在一致的浏览器环境中捕获每个 UI 组件的图像。新的屏幕截图会自动与之前接受的基线屏幕截图进行比较。当存在视觉差异时,您会收到通知。

Visual test components

如果您正在构建现代 UI,可视化测试可以使您的前端团队免于耗时的手动审查,并防止昂贵的 UI 回归。

上一章中,我们学习了如何使用 Chromatic 发布 Storybook。我们在每个 Button 组件周围添加了一个粗体的红色边框,然后向队友请求反馈。

Button red border

现在,让我们看看如何使用 Chromatic 的内置测试工具进行可视化测试。当我们创建 pull request 时,Chromatic 捕获了我们更改的图像,并将它们与同一组件的先前版本进行了比较。发现了四个更改

List of checks in the pull request

单击 🟡 UI 测试复选框以查看它们。

Second build in Chromatic with changes

查看它们以确认它们是有意的(改进)还是无意的(错误)。如果您接受更改,测试基线将更新。这意味着后续提交将与新的基线进行比较以检测错误。

Reviewing changes in Chromatic

在上一章中,我们的队友出于某种原因不希望 Button 周围有红色边框。拒绝更改以表明它们需要撤消。

Review deny in Chromatic

撤消更改并再次提交以再次通过您的可视化测试。

交互测试

到目前为止,我们已经了解了可视化测试如何使我们能够抽查外观并捕获 UI 回归。但是,随着我们继续开发我们的设计系统,我们的组件最终将负责更多不仅仅是渲染 UI 的任务。在某些时候,它们将处理状态管理甚至获取数据。这就是测试组件交互将帮助我们的地方。

交互测试是验证用户行为的一种众所周知的模式。您首先提供模拟数据来设置测试,使用测试库模拟用户交互,并验证 UI 更改。在 Storybook 中,这发生在浏览器中,使调试故障更容易,因为您在与开发组件相同的环境中运行测试:浏览器。

为了启用它,我们将依赖 Storybook 的 play 函数和检测工具化的测试库来设置我们的测试,然后使用 test-runner 来验证我们的组件是否正确渲染。

设置测试运行器

首先添加必要的依赖项,使用

复制
yarn add --dev @storybook/test-runner

接下来,在您的 package.json 脚本中添加一个新的测试任务

{
  "scripts": {
    "test-storybook": "test-storybook"
  }
}

使用 play 函数编写交互测试

交互测试围绕 UI 如何处理用户操作展开,无论是使用键盘、鼠标还是其他输入设备,并检查 UI 视觉元素是否显示和工作正常。诸如 Jest 之类的测试库提供了有用的 API,用于模拟人机交互和验证 UI 状态。我们将使用这些工具的检测工具化版本来编写我们的测试。因此,保持通用语法,但具有额外的遥测功能来帮助我们调试。

测试本身是在连接到故事的 play 函数内部定义的。它们是在故事渲染后运行的小段代码。

让我们通过更新 Button 故事并添加以下内容来设置我们的第一个交互测试,看看它是如何工作的

复制
src/Button/Button.stories.jsx
import styled from '@emotion/styled';

import { Button } from './Button';
import { Icon } from '../Icon/Icon';
import { StoryLinkWrapper } from '../LinkWrapper';

+ import { expect, userEvent, within } from '@storybook/test';

export default {
  title: 'Design System/Button',
  component: Button,
};

// Other Button stories

/*
 * New story using the play function.
 * See https://storybook.org.cn/docs/react/writing-stories/play-function
 * to learn more about the play function.
 */
+ export const WithInteractions = {
+   args: {
+     appearance: 'primary',
+     href: 'https://storybook.org.cn',
+     ButtonWrapper: StoryLinkWrapper,
+     children: 'Button',
+   },
+   play: async ({ canvasElement }) => {
+     // Assigns canvas to the component root element
+     const canvas = within(canvasElement);
+     await userEvent.click(canvas.getByRole('link'));
+     expect(canvas.getByRole('link')).toHaveAttribute(
+       'href',
+       'https://storybook.org.cn',
+     );
+   },
+ };

💡 @storybook/test 包取代了 @storybook/jest@storybook/testing-library 测试包,提供了更小的捆绑包大小和基于 Vitest 包的更直接的 API。

当 Storybook 完成故事渲染时,它会执行在 play 函数内部定义的步骤,与组件进行交互,类似于用户执行操作的方式。单击 Interactions 面板。您将看到详细的执行流程,同时还提供了一组方便的 UI 控件,用于暂停、恢复、倒带和单步执行每个交互。

使用测试运行器自动化测试

我们已经了解了使用 play 函数的交互测试如何帮助我们验证组件在与它交互时的响应方式。但是,随着设计系统的发展,手动验证每个更改可能很快变得不切实际。Storybook 测试运行器自动化了这个过程。它是一个独立的实用程序——由 Playwright 提供支持——与您的 Storybook 并行运行,执行所有交互测试并捕获损坏的故事。

在 Storybook 运行时,打开一个新的终端窗口并使用以下命令运行测试运行器

复制
yarn test-storybook --watch

Storybook test runner execution

它将验证我们所有的故事是否在没有错误的情况下渲染,以及所有的断言是否在执行期间自动通过。更重要的是,如果测试失败,它将为我们提供一个链接,该链接将在浏览器中打开失败的故事。

在 CI 中运行交互测试

使用 play 函数的交互测试以及使用测试运行器进行自动化帮助我们模拟用户交互并验证组件的 UI 状态。但是,即使我们的设计系统不断发展,在本地运行它们也可能是一项耗时且重复的任务。再次强调,这就是 CI 的用武之地。让我们看看如何使用我们现有的 CI 工作流程来设置它。

更新我们在上一章中创建的现有工作流程,并按如下方式启用交互测试

复制
.github/workflows/chromatic.yml
# Name of our action
name: 'Chromatic'
# The event that will trigger the action
on: push

# What the action will do
jobs:
  # Run interaction tests
  interaction-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v3
        with:
          #👇 Sets the version of Node.js to use
          node-version: 16
      - name: Install dependencies
        run: yarn
      - name: Install Playwright
        run: npx playwright install --with-deps
      - name: Build Storybook
        run: yarn build-storybook --quiet
      - name: Serve Storybook and run tests
        run: |
          npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \
            "npx http-server storybook-static --port 6006 --silent" \
            "npx wait-on tcp:6006 && yarn test-storybook"
  visual-tests:
    # The operating system it will run on
    runs-on: ubuntu-latest
    # The list of steps that the action will go through
    steps:
      - uses: actions/checkout@v2
        with:
          #👇 Fetches all history so Chromatic can compare against previous builds
          fetch-depth: 0
      - uses: actions/setup-node@v3
        with:
          #👇 Sets the version of Node.js to use
          node-version: 16
      - run: yarn
        #👇 Adds Chromatic as a step in the workflow
      - uses: chromaui/action@v1
        # Options required for Chromatic's GitHub Action
        with:
          #👇 Chromatic projectToken, see https://storybook.org.cn/tutorials/design-systems-for-developers/react/en/review/ to obtain it
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          token: ${{ secrets.GITHUB_TOKEN }}

我们的工作流程将在代码推送到我们设计系统存储库的任何分支时运行,并且它将有两个作业;一个用于交互测试,一个用于可视化测试。交互测试作业首先构建 Storybook,然后运行测试运行器,通知我们损坏的测试。可视化测试作业将像以前一样运行,运行 Chromatic 以验证我们组件的视觉状态。

可访问性测试

“可访问性意味着所有人,包括残疾人在内,都可以理解、导航和与您的应用程序交互……在线[示例包括]访问内容的其他方式,例如使用 Tab 键和屏幕阅读器来遍历站点。” T.Rowe Price 的开发人员 Alex Wilson 写道

根据世界卫生组织的数据,残疾影响了 15% 的人口。设计系统对可访问性具有巨大的影响,因为它们包含用户界面的构建块。仅仅改进一个组件的可访问性意味着您公司中该组件的每个实例都受益。

Storybook accessibility addon

使用 Storybook 的 Accessibility 插件抢先体验包容性 UI,这是一个用于验证 Web 可访问性标准 (WCAG) 的实时工具。

复制
yarn add --dev @storybook/addon-a11y

更新您的 Storybook 配置以包含该插件。

复制
.storybook/main.js
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
+   '@storybook/addon-a11y',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};
export default config;

一旦全部设置完成,您将在 Storybook 插件面板中看到一个新的“Accessibility”选项卡。

Storybook a11y addon

它向您显示 DOM 元素的可访问性级别(违规和通过)。单击“突出显示结果”复选框以在 UI 组件中可视化违规。

从这里开始,遵循插件的可访问性建议。

其他测试策略

矛盾的是,测试可以节省时间,但也可能因维护而降低开发速度。明智地测试正确的事情——而不是所有事情。即使软件开发有许多测试策略,我们也很痛苦地发现,有些策略不适合设计系统。

代码覆盖率测试

代码覆盖率测试衡量您的代码库中有多少被测试覆盖。它们是确保您的测试实际测试某些内容的好方法。但是,它们不是衡量测试质量的好方法,但它们可以有益于验证设计系统提供的所有组件和实用程序是否按预期运行,从而帮助识别设计系统实现中的任何潜在差距或问题。Storybook 提供了一个 addon 来帮助我们实现这一点。Storybook coverage addon 由 Istanbul 提供支持,为您的 Storybook 故事生成代码覆盖率报告。让我们看看如何操作。

首先运行以下命令来安装 coverage 插件

复制
yarn add --dev @storybook/addon-coverage

接下来,更新你的 Storybook 配置以包含该插件

复制
.storybook/main.js
/** @type { import('@storybook/react-vite').StorybookConfig } */
const config = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
+   '@storybook/addon-coverage',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
};
export default config;

最后,在 Storybook 运行的情况下,使用 --coverage 标志启动测试运行器(在单独的终端窗口中)

复制
yarn test-storybook --coverage

Storybook coverage tests report

从这里开始,按照建议改进你的代码覆盖率。

快照测试 (Jest)

此技术捕获 UI 组件的代码输出,并将其与以前的版本进行比较。测试 UI 组件标记最终会测试实现细节(代码),而不是用户在浏览器中的体验。

差异代码快照是不可预测的,并且容易产生误报。在组件级别,代码快照不考虑全局更改,例如设计令牌、CSS 和第三方 API 更新(网络字体、Stripe 表单、Google Maps 等)。在实践中,开发人员会求助于“全部批准”或完全忽略快照测试。

大多数组件快照测试实际上只是屏幕截图测试的更糟糕版本。测试你的输出。快照渲染的内容,而不是底层的(易变的!)标记。– Mark Dalgliesh,SEEK 的前端基础设施,CSS 模块创建者

端到端测试 (Selenium, Cypress)

端到端测试遍历组件 DOM 以模拟用户流程。它们最适合验证应用程序流程,如注册或结账流程。功能越复杂,这种测试策略就越有用。

设计系统包含功能相对简单的原子组件。验证用户流程对于此任务来说通常是过度的,因为测试创建起来耗时且难以维护。但是,在极少数情况下,组件可能会从端到端测试中受益。例如,验证复杂的 UI,如日期选择器或独立的支付表单。

通过文档推动采用

仅靠测试并不能使设计系统完整。由于设计系统为来自整个组织的利益相关者服务,我们需要教导其他人如何从我们经过良好测试的 UI 组件中获得最大收益。

在第 6 章中,我们将学习如何通过文档加速设计系统的采用。了解为什么 Storybook Docs 是创建内容全面的文档且事半功倍的秘密武器。

让你的代码与本章保持同步。在 GitHub 上查看 f5a815f。
这个免费指南对您有帮助吗? 发推文以示赞赏并帮助其他开发人员找到它。
下一章
文档
通过文档推动设计系统的采用
✍️ 在 GitHub 上编辑 – 欢迎 PR!
加入社区
6,721位开发者及更多
为什么为什么 Storybook组件驱动的 UI
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI