可视化 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 维护者提供的免费可视化测试服务。