工作流程
开发用户界面一直以来都没有明确的定义。UI 的主观性导致了临时性的开发工作流程和易出错的 UI。本章将分享专业团队如何以严格的视觉测试驱动方式构建 UI。
测试驱动开发
在我们开始之前,先回顾一下一个流行的工程实践:**测试驱动开发 (TDD)**。TDD 的核心思想是在开发被测试功能之前先编写测试。
- 为你的代码构建一套自动化单元测试
- 编写代码本身来“让测试通过”
TDD 让你能够清晰地思考你的代码需要做什么,这体现在具体的输入(对于组件来说,我们称之为“状态”)上。这样,你就可以覆盖模块的所有用例。
让我们来看一个例子。假设我们有一个 relativize
函数,它将原始日期对象转换为“2 周前”这样的相对日期格式。列出所有你想要覆盖的各种输入类型是相当直接的。然后,只需每次你认为自己在向解决方案取得进展时,点击“测试”按钮即可。
你的测试框架允许你在隔离的环境中运行 relativize
函数,而无需为整个应用提供输入只是为了测试那一部分。
然而,在开发 UI 时,TDD 就显得不足了,因为很难提前定义测试,模块难以隔离,并且输出是主观的。这些缺点可以通过在隔离的环境中对组件进行视觉测试来解决。
视觉测试
UI 测试的棘手之处在于,仅凭代码不可能验证相关的视觉细节。视觉测试通过快速且专注地引入人工判断来规避了这一点。
视觉测试工作流程
在实践中,视觉测试使用 Storybook 在一组定义的测试状态下“视觉上”测试组件。视觉测试与其他任何类型的测试共享相同的设置、执行和拆解步骤,但验证步骤由用户负责。
test do
setup
execute 👈 Storybook renders stories
verify 👈 you look at stories
teardown
end
随后,通过自动捕获和比较图像快照来捕获任何回归。
test do
setup
execute 👈 Storybook renders stories
verify 👈 capture image snapshots and compare them to baselines
teardown
end
在两种情况下使用相同的测试用例,只改变了验证方法。
如何编写视觉测试用例
现在,让我们专注于第一种情况。在 Storybook 中,一个测试就像渲染一个 React 元素一样简单。要编写一个视觉测试用例,在 Storybook 行话中称为“故事”,我们需要概述我们感兴趣的组件状态。下面的代码示例展示了如何为 InboxTask
、SnoozedTask
和 PinnedTask
编写视觉测试。
import type { Meta, StoryObj } from '@storybook/react';
import Task from './Task';
const meta: Meta = {
component: Task,
title: 'Task',
} satisfies Meta<typeof Task>;
export default meta;
type Story = StoryObj<typeof meta>;
export const InboxTask: Story = {
args: {
task: {
id: '1',
title: 'Test Task',
state: 'TASK_INBOX',
updatedAt: new Date(2023, 0, 1, 9, 0),
boardName: 'On Test Board',
},
},
};
export const SnoozedTask: Story = {
args: {
task: {
// Shaping the stories through args composition.
...InboxTask.args?.task,
state: 'TASK_SNOOZED',
},
},
};
export const PinnedTask: Story = {
args: {
task: {
// Shaping the stories through args composition.
...InboxTask.args?.task,
state: 'TASK_PINNED',
},
},
};
在 Storybook 中,Task
及其变体将出现在侧边栏中。这对应于测试周期的“执行”阶段;“验证”阶段我们在 Storybook 中通过肉眼进行。
对于 UI 测试,人工验证是一种实用的方法,因为它对组件中不影响视觉外观的代码更改具有鲁棒性。此外,由于我们只需要提前编写输入并目视检查输出,我们就是在以 TDD 风格自动构建 UI。
学习视觉测试驱动开发
如果你是基于一个深思熟虑的设计来构建应用,很可能在设计产物中嵌入了一组带有明确输入和输出的组件。将这个“设计规范”与视觉测试过程结合起来,你就可以进行与 TDD 完全类似的实践。
在下一章中,我们将通过使用视觉 TDD 编写一个示例组件来应用我们目前所学的知识。