继讨论过“前端写不写单元测试”与“React应用测试:配置Typescript, Jest, ESLint”之后,我们开始进入写测试的环节。
选型:
- “React应用测试:配置Typescript, Jest, ESLint”中的配置
- @testing-library/react 库
没有其他附加的断言库和框架。
简单的渲染测试
组件测试第一步,先看看组件是不是能根据提供的属性正常显示。
假设我们有这样一个简单的组件MyComponent.tsx:
import * as React from 'react';
export default function MyComponent() {
return <div>test</div>;
}
在这个组件旁边创建一个 MyComponent.test.tsx测试文件(打包工具如Webpack不会将它打包进去):
import * as React from 'react';
import { render } from '@testing-library/react';
import MyComponent from './MyComponent';
test('test render', () => {
const { getByText } = render(<MyComponent />);
expect(getByText('test')).toBeTruthy();
});
测试完美通过。这里用了@testing-library/react的两个接口:
render()用来渲染React组件- 由
render()返回的getByText()是个匹配方法,可以找到有test这个text node的React/DOM元素
这里我将有没有显示test这个词作为测试通过的关键检查点,而不是元素ID或者是CSS Class,因为这样更贴合实际需求的要求。
就这样通过好像有一点不真实。那测试大神 Kent C有个非常有意思的提议——除了让你的测试通过之外,你必须看看是不是不符合要求的时候测试能报错,如果不报错,那说明测试写的不到位。
所以让我们给test随意加一个尾巴变成test1试下:

如我们预期的,测试失败了。并且@testing-library/react很贴心地帮我们打印出了实际渲染出的DOM结构,以供我们后续调试。
匹配方法
这个render()除了返回getByText()之外,还有一系列方便使用的其他匹配方法:
| 方法前缀 | 返回 | 特征 |
|---|---|---|
| getBy | 匹配的元素 | 无匹配或有多个匹配到的元素会报错 |
| getAllBy | 匹配的元素组 | 匹配不到会报错 |
| findBy | Promise,resolve为匹配元素 | 尝试等这个元素出现,默认最多等1秒,过期匹配不到就reject |
| findAllBy | Promise,resolve为匹配元素组 | 同上 |
| queryBy | 第一个匹配到的元素或null | 匹配到多个元素会报错 |
| queryAllBy | 匹配的元素组或空数组 | - |
(其实我感觉queryBy和queryAllBy有点多余……)
注意以上只是方法名的前缀,实际使用时需要搭配目标后缀:
- ByLabelText - 匹配表单Label的文字内容
- ByPlaceholderText - 匹配表单元素的占位(placeholder)文字
- ByText - 匹配元素文字内容
- ByAltText - 匹配元素的
alt属性 - ByTitle - 匹配元素的
title属性 - ByDisplayValue - 匹配表单元素的值(value)
- ByRole - 匹配元素的
role属性 - ByTestId - 匹配元素的
data-testid属性,通常为一些特殊的测试要求特意添加的一个ID
上一节使用的getByText()方法就是getBy与ByText的组合。
当然你也完全可以用.querySelectorAll()自己写,这些方法只是提供了一些便捷性。
它们其实是从@testing-library/react的同胞@testing-library/dom继承而来,在render()方法中返回便于使用。
测试渲染的更新
如果一个组件可以接收一些参数,那这时除了能根据初始传入的属性渲染之外,最好再验证一下组件是否能根据参数的变化更新相应的内容。
假设我们的组件MyComponent.tsx现在能够接收一个参数:
import * as React from 'react';
export default function MyComponent({ n }: { n: number }) {
return <div>test{n}</div>;
}
更新MyComponent.test.tsx测试文件:
import * as React from 'react';
import { render } from '@testing-library/react';
import MyComponent from './MyComponent';
test('test re-render', () => {
const { getByText, rerender } = render(<MyComponent n={1} />);
expect(getByText('test1')).toBeTruthy();
rerender(<MyComponent n={2} />);
expect(getByText('test2')).toBeTruthy();
});
以上,我们先尝试用n=1为输入值初始化渲染,然后用rerender()方法将组件的属性改为n=2再重新验证渲染的内容。
小结
以上我们用@testing-library/react对一个异常简单的React组件进行了渲染关键内容的测试。这在实际使用中这些是远远不够的,我们在遇到用CSS Module,第三方组件,复杂依赖的时候需要做一些其他的配置来配合测试,否则Jest会有各种奇怪的抱怨。
好了,我们下次再见👋!
看看其他测试系列文:
- 前端写不写单元测试?| 创作者训练营
- React应用测试:配置Typescript, Jest, ESLint | 创作者训练营
- Jest测试CSS module组件报错怎么办?
- 用Jest mock隔离单元测试中的不可控因素
- 用react-testing-library快速测试React组件交互