工作流
用户界面的开发一直是一个定义模糊的领域。UI 的主观性导致了临时的开发工作流程和有缺陷的 UI。本章将分享专业团队如何以严谨、面向视觉测试的方式构建 UI。
面向测试的开发
在我们开始之前,让我们回顾一下 面向测试的开发 (TDD),这是一项流行的工程实践。TDD 的核心思想是,你在开发被测试的功能之前就编写测试。
- 为你的代码构建一组自动化的单元测试
- 编写代码本身以“使测试通过”
TDD 允许你清晰地思考你的代码需要做什么,具体来说就是针对具体的输入(对于组件,我们称之为“状态”)。这样,你就可以涵盖你模块的所有用例。
让我们看一个例子。假设我们有一个 relativize 函数,它将原始日期对象转换为相对日期格式,形如“2 周前”。要列出所有你想涵盖的各种输入类型是很直接的。然后,每次你认为自己在解决方案上取得进展时,只需点击“测试”按钮。
你的测试框架允许你独立运行 relativize 函数,而无需为你的整个应用程序提供输入,只为了测试那一部分。
然而,TDD 在开发 UI 时会遇到困难,因为提前定义测试很困难,模块难以隔离,而且输出是主观的。通过对组件进行隔离的视觉测试可以解决这些缺点。
视觉测试
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-vite';
import Task from './Task';
const 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 来编码一个示例组件。