前言
在上一篇中我们已经对组件单测有了初步的经验,接下来我们针对组件的单测再补充改一些常见的case
判断样式
在react component中,常见会用一些字段来控制一些样式,或者通过props传入一些style,我们通过单测,要判断到这些样式是否正确的渲染出来
修改组件
为TodoHeader
组件增加多一个containerStyle
的入参,用于控制最外层容器的样式,这里入参我们设置为optional就好,非必传
import "./index.css";
interface TodoHeaderProps {
// 待办事项的标题
title: string;
containerStyle?: React.CSSProperties;
}
export default function TodoHeader({ title, containerStyle }: TodoHeaderProps) {
return (
<div className="report-header" style={containerStyle}>
<span className="title" data-testid="todo-header-title">
{title}
</span>
</div>
);
}
调用修改,增加样式入参
<TodoHeader
title="这是一个标题"
containerStyle={{ border: "1px solid blue" }}
/>
我们运行一下看看效果,pnpm start
,可以看到border
样式正确渲染了出来
单测编写
it(`正确渲染containerStyle的样式`, () => {
const borderStyle = "1px solid blue";
const containerStyle: CSSProperties = {
border: borderStyle,
};
const { container } = render(
<TodoHeader title="标题" containerStyle={containerStyle} />
);
expect(container.children[0]).toHaveStyle(`border: ${borderStyle}`);
});
这里我们用到了一个api,toHaveStyle
来判断我们的样式是否有渲染出来,因为我们的样式是挂载在最顶层的div,所以我们直接用container.children[0]
来获取这个div即可,当然可以换成跟我们上一篇文章里面用到的方法一样。
然后我们执行一下这个测试用例
pnpm test src/components/__tests__/todo-header.test.tsx
结果如下:
我们来试试失败的情况,如果现在我们把样式改了,看看这个用例是否会执行失败,这里改了入参时候的样式,改为了2px
it(`正确渲染containerStyle的样式`, () => {
const borderStyle = "1px solid blue";
const containerStyle: CSSProperties = {
border: "2px solid blue",
};
const { container } = render(
<TodoHeader title="标题" containerStyle={containerStyle} />
);
expect(container.children[0]).toHaveStyle(`border: ${borderStyle}`);
});
看下运行结果,可以看到expected和received不符合,用例没有通过(这里我们是为了测试所以特地改了,我们还是改回去)
判断字段控制样式
在实际项目中我们会根据传入一些字段然后来区分展示不同的样式,最常见的就是各种状态展示不同的样式,这里我们也模拟一下这种场景
修改组件
改写一下之前的代码,增加多一个isFinish
的字段,通过这个字段来显示不同的background
import "./index.css";
interface TodoHeaderProps {
// 待办事项的标题
title: string;
// 最外层容器的样式
containerStyle?: React.CSSProperties;
// 是否结束
isFinish?: boolean;
}
export default function TodoHeader({
title,
containerStyle,
isFinish = false,
}: TodoHeaderProps) {
return (
<div className="report-header" style={containerStyle}>
<span
className="title"
data-testid="todo-header-title"
style={{ background: isFinish ? "red" : "white" }}
>
{title}
</span>
</div>
);
}
改一下调用的入口代码,看看效果,可以看到效果是有了(样式啥的就不吐槽了,毕竟只是为了做单测写的)
<TodoHeader
title="这是一个标题"
containerStyle={{ border: "1px solid blue" }}
isFinish={true}
/>
单测编写
这里直接就是getByTestId
加toHaveStyle
的组合就可以了
it(`正确渲染isFinish为true的样式`, () => {
const { getByTestId } = render(<TodoHeader title="标题" isFinish={true} />);
const element = getByTestId("todo-header-title");
expect(element).toHaveStyle(`background: red`);
});
运行结果
这里不要忘了,我们还有一种为false的情况,因为我们的
isFinish
是boolean,只有true或者false两种情况,我们上面已经测试过true的情况了,我们再保证一下false的情况下也正常渲染
it(`正确渲染isFinish为false的样式`, () => {
const { getByTestId } = render(
<TodoHeader title="标题" isFinish={false} />
);
const element = getByTestId("todo-header-title");
expect(element).toHaveStyle(`background: white`);
});
运行结果
判断属性
在react component中,给元素设置一些属性也是很常见的一种情况,比如我们传入图片url然后在组件中正确显示出来,根据这种情况我们就可以写一些单测
改写组件
我们增加多一个iconUrl
的入参,如果有传入的时候我们就通过img标签把它显示出来,看下代码
import "./index.css";
interface TodoHeaderProps {
// 待办事项的标题
title: string;
// 最外层容器的样式
containerStyle?: React.CSSProperties;
// 是否结束
isFinish?: boolean;
// 图标的链接
iconUrl?: string;
}
export default function TodoHeader({
title,
containerStyle,
iconUrl,
isFinish = false,
}: TodoHeaderProps) {
return (
<div className="report-header" style={containerStyle}>
{iconUrl && <img src={iconUrl} />}
<span
className="title"
data-testid="todo-header-title"
style={{ background: isFinish ? "red" : "white" }}
>
{title}
</span>
</div>
);
}
单测编写
这里用到了toHaveAttribute
,得注意下这个api的入参,还用到了waitFor
这个api,这里我们先不展开讨论,后面会详细说一说
it(`正确渲染显示图标的情况`, async () => {
const iconUrl = "http://www.abc.com/test.png";
const { container } = render(<TodoHeader title="标题" iconUrl={iconUrl} />);
await waitFor(() => {
const imgElement = container.querySelector("img");
expect(imgElement).not.toBeNull();
expect(imgElement).toHaveAttribute("src", iconUrl);
});
});
运行结果
判断children
children在组件中也是比较常见的,给了组件多一些类似于插槽的功能,这里简单讲下我判断children的方法,其实思路比较简单,只不过是传入children之后通过获取元素判断其能否正常显示出来即可
改写组件
直接用PropsWithChildren
增加了children的入参,显示children
import "./index.css";
import { PropsWithChildren } from "react";
interface TodoHeaderProps {
// 待办事项的标题
title: string;
// 最外层容器的样式
containerStyle?: React.CSSProperties;
// 是否结束
isFinish?: boolean;
// 图标的链接
iconUrl?: string;
}
export default function TodoHeader({
title,
containerStyle,
iconUrl,
isFinish = false,
children,
}: PropsWithChildren<TodoHeaderProps>) {
return (
<div className="report-header" style={containerStyle}>
{iconUrl && <img src={iconUrl} />}
<span
className="title"
data-testid="todo-header-title"
style={{ background: isFinish ? "red" : "white" }}
>
{title}
</span>
{children}
</div>
);
}
单测编写
it(`正确渲染children的情况`, async () => {
const id = "childrenId";
const text = "这是一个文案";
const { getByTestId } = render(
<TodoHeader title="标题">
<span data-testid={id}>{text}</span>
</TodoHeader>
);
const childElement = getByTestId(id);
expect(childElement).toHaveTextContent(text);
});
运行结果
判断ReactNode
和children比较类似,判断的方法也基本一致
改写组件
import "./index.css";
import { PropsWithChildren, ReactNode } from "react";
interface TodoHeaderProps {
// 待办事项的标题
title: string;
// 最外层容器的样式
containerStyle?: React.CSSProperties;
// 是否结束
isFinish?: boolean;
// 图标的链接
iconUrl?: string;
// 额外的信息
extraInfo?: ReactNode;
}
export default function TodoHeader({
title,
containerStyle,
iconUrl,
isFinish = false,
children,
extraInfo,
}: PropsWithChildren<TodoHeaderProps>) {
return (
<div className="report-header" style={containerStyle}>
{iconUrl && <img src={iconUrl} />}
<span
className="title"
data-testid="todo-header-title"
style={{ background: isFinish ? "red" : "white" }}
>
{title}
</span>
<span className="extra">{extraInfo}</span>
{children}
</div>
);
}
单测编写
it(`正确渲染extraInfo的情况`, async () => {
const id = "extraId";
const text = "这是一个文案";
const { getByTestId } = render(
<TodoHeader
title="标题"
extraInfo={<span data-testid={id}>{text}</span>}
/>
);
const childElement = getByTestId(id);
expect(childElement).toHaveTextContent(text);
});
运行结果
结尾
本篇博客补充了其他情况的一些组件单测情况,并把自己的一些经验写到了demo中,完整的代码可以看看这个commit
传送门
前端单测学习(1)—— 单测入门之react单测项目初步
前端单测学习(2)—— react 组件单测初步
前端单测学习(3)—— react组件单测进阶
前端单测学习(4)—— react 组件方法&fireEvent
前端单测学习(5)—— 快照
前端单测学习(6)—— 定时器
前端单测学习(7)—— mock
前端单测学习(8)—— react hook
前端单测学习(9)—— 覆盖率报告
前端单测学习(10)—— 状态管理redux
前端单测学习(11)—— react hook 进阶
前端单测学习(12)—— 性能优化
前端单测学习(13)—— 自动化测试
代码仓库: github.com/liyixun/rea…