可视化 TDD
基础知识介绍完毕,现在让我们深入细节。本示例将展示如何使用 Storybook 通过 可视化 TDD 构建 CommentList
组件的各种状态。
- 构建可视化测试用例
- 在 Storybook 中检查测试
- 构建实现
- 对照设计稿检查实现
- 迭代
我们正在构建什么
CommentList
是一个用于星际自由战士的聊天工具的一部分。我们的设计师根据数据和应用状态为评论列表的各种外观提供了设计稿。我们的任务是确保该列表在精确的文本、显示的图像和视觉处理方面正确渲染。
1. 构建可视化测试用例
通过构建测试用例来启动可视化 TDD。我们将创建三个与上面三张图片相匹配的用例。严格的 TDD 实践者会说我们需要一次开发和实现一个测试用例;这取决于你是否认为这对你的流程有帮助。
让我们使用 degit 设置示例项目,以下载必要的样板模板(带有部分默认配置的已构建应用程序)。运行以下命令
# Clone the template for this tutorial
npx degit chromaui/visual-testing-handbook-react-template commentlist
cd commentlist
# Install dependencies
yarn
接下来,我们将构建最简单的 CommentList
实现,以确保我们的测试设置正确。
在你的 src
目录中,创建一个名为 components
的新文件夹,然后创建一个名为 CommentList.tsx
的新文件,其内容如下
interface Author {
name: string;
avatar: string;
}
interface Comment {
text: string;
author: Author;
}
export interface CommentListProps {
/**
* Is the component in the loading state
*/
loading?: boolean;
/**
* Total number of comments
*/
totalCount?: number;
/**
* List of comments
*/
comments?: Comment[];
}
/**
* The Commentlist component should display the comments from the users.
*/
export default function CommentList({
loading = false,
comments = [],
totalCount = 10,
}: CommentListProps) {
if (loading) {
return <div>loading</div>;
}
if (comments.length === 0) {
return <div>empty</div>;
}
return (
<div>
{comments.length} of {totalCount}
</div>
);
}
现在我们有了一个基本实现,可以构建我们的测试状态了。Storybook 使这变得快速简便。
在 src/components
中创建一个名为 CommentList.stories.ts
的新文件,并添加以下内容
import type { Meta, StoryObj } from '@storybook/react';
import CommentList from './CommentList';
const meta = {
component: CommentList,
title: 'CommentList',
} satisfies Meta<typeof CommentList>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Paginated: Story = {
args: {
comments: [
{
text: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.',
author: {
name: 'Luke',
avatar: 'luke.jpeg',
},
},
{
text: 'Ut enim ad minim veniam, quis nostrud exercitation ullamco.',
author: {
name: 'Leah',
avatar: 'leah.jpeg',
},
},
{
text: 'Duis aute irure dolor in reprehenderit in voluptate.',
author: {
name: 'Han',
avatar: 'han.jpeg',
},
},
{
text: 'Ut enim ad minim veniam, quis nostrud exercitation ullamco.',
author: {
name: 'Poe',
avatar: 'poe.jpeg',
},
},
{
text: 'Duis aute irure dolor in reprehenderit in voluptate.',
author: {
name: 'Finn',
avatar: 'finn.jpeg',
},
},
],
totalCount: 10,
},
};
export const HasData: Story = {
args: {
comments: [...(Paginated?.args?.comments?.slice(0, 3) || [])],
totalCount: 3,
},
};
export const Loading: Story = {
args: {
comments: [],
loading: true,
},
};
export const Empty: Story = {
args: {
...Loading.args,
loading: false,
},
};
2. 在 Storybook 中检查测试
启动 Storybook 查看测试用例。我们的组件实现很简单,但这足以让我们确认测试用例是否按预期渲染。
yarn storybook
3. 构建实现
到目前为止,我们已经搭建了一个基本的实现,然后设置 Storybook 来渲染我们的测试用例。是时候开始独立构建 HasData
变体的实现了。
我们使用 styled-components
– 一个在组件级别封装 CSS 的库。运行以下命令
yarn add styled-components
将你的 CommentList.tsx
文件更新为以下内容
+ import styled, { createGlobalStyle } from 'styled-components';
interface Author {
name: string;
avatar: string;
}
interface Comment {
text: string;
author: Author;
}
export interface CommentListProps {
/**
* Is the component in the loading state
*/
loading?: boolean;
/**
* Total number of comments
*/
totalCount?: number;
/**
* List of comments
*/
comments?: Comment[];
}
+ const CommentListWrapper = styled.div`
+ font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: #333;
+ display: inline-block;
+ vertical-align: top;
+ width: 265px;
+ `;
+ const CommentItem = styled.div`
+ font-size: 12px;
+ line-height: 14px;
+ clear: both;
+ height: 48px;
+ margin-bottom: 10px;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 0 10px 0;
+ background: linear-gradient(
+ 120deg,
+ rgba(248, 248, 254, 0.95),
+ rgba(250, 250, 250, 0.95)
+ );
+ border-radius: 48px;
+ `;
+ const Avatar = styled.div`
+ float: left;
+ position: relative;
+ overflow: hidden;
+ height: 48px;
+ width: 48px;
+ margin-right: 14px;
+ background: #dfecf2;
+ border-radius: 48px;
+ `;
+ const AvatarImg = styled.img`
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ left: 0;
+ top: 0;
+ z-index: 1;
+ background: #999;
+ `;
+ const Message = styled.div`
+ overflow: hidden;
+ padding-top: 10px;
+ padding-right: 20px;
+ `;
+ const Author = styled.span`
+ font-weight: bold;
+ `;
+ const CommentText = styled.span``;
+ const GlobalStyle = createGlobalStyle`
+ @import url('https://fonts.googleapis.com/css?family=Nunito+Sans:400,400i,800');
+ `;
/**
* The Commentlist component should display the comments from the user.
*/
export default function CommentList({
loading = false,
comments = [],
totalCount = 10,
}: CommentListProps) {
if (loading) {
return <div>loading</div>;
}
if (comments.length === 0) {
return <div>empty</div>;
}
return (
+ <>
+ <GlobalStyle />
+ <CommentListWrapper>
+ {comments.map(({ text, author: { name, avatar } }) => (
+ <CommentItem key={`comment_${name}`}>
+ <Avatar>
+ <AvatarImg src={avatar} />
+ </Avatar>
+ <Message>
+ <Author>{name}</Author> <CommentText>{text}</CommentText>
+ </Message>
+ </CommentItem>
+ ))}
+ </CommentListWrapper>
+ </>
);
}
4. 对照设计稿检查实现
在 Storybook 中检查组件的外观。本示例已经提供了 CSS,但在实践中,我们会随着进度调整样式并在 Storybook 中确认它们。
5. 迭代
如果我们对步骤 4 中的实现不满意,我们会回到步骤 3 继续修改。如果 UI 与规范匹配,那么我们将继续构建下一个变体——例如向 Paginated
story 中添加“加载更多”按钮。
在这个工作流程中迭代时,定期检查每个 story,以确保最终实现正确处理了每个测试状态,而不仅仅是我们最后修改的那个。
学习如何自动化可视化测试
在下一章中,我们将看到如何使用 Chromatic 自动化 VTDD 流程,Chromatic 是一个由 Storybook 维护者创建的免费可视化测试服务。