第二部分 - 测试篇:给组件加上单元测试
引言
在第一部分,我们基于 create-react-app
构建了项目的基础结构。对于一个组件库来说,不仅要有简单,实用的组件,每一个组件的质量也是至关重要。而单元测试,正是提升软件质量的一种有效的手段。在本文中,不仅会在之前的项目中完成单元测试的配置,还会带着大家一起走进单元测试的世界~
教程部分
本篇文章,是这个系列的第二篇 - 单元测试
2020到了,我还需要单元测试吗?
首先,告诉大家一个小秘密,就是我不太喜欢单元测试。它很费时,重构项目会变得很吃力。而且,单元测试没有一个明确的边界,有时总会觉得自己的单元测试写的不够。而且,代码也并不会因为单元测试就不会出现 BUG
,即使你的测试覆盖率达到了?%。
值得幸运的是,对于 React
应用来说,单元测试的成本正在逐步降低。
优秀的 TypeScript
首先,在 TypeScript
出现之后,我们不需要去写大部分的单元测试。
举个简单的例子,这是 TypeScript
的实现:
type Props = {
name: string;
};
而下面的这段代码,是单元测试的版本:
import React from 'react';
import Component from './Component';
test('does not do something completely embarassing if I forgot to pass a name', () => {
const { getByText } = render(<Component />);
expect(getByText('Name: ')).not.toBeInTheDocument();
});
所以,TypeScript
的类型系统的出现,已经不再需要这部分的单元测试了。
ESLint 和 Prettier
Eslint
会分析代码,并根据一组规则对其进行检查。这听起来也很像测试。而 Prettier
可以根据一组规则来约束代码风格。并且,在编写代码的时候,配合编辑器就可以得到即时的反馈了。
像用户一样编写测试
上面所提到的技术,更多是从语言,代码质量,代码风格的角度来提升软件的质量,但却没有像一个用户一样来使用我们的软件。毕竟,软件怎么使用,只有开发者清楚。
除了编写文档、注释、编写 Demo。单元测试也更像是软件的一种说明书,为软件的使用提供了保障。
和 React 文档 中所描述的一样,本文也只是讨论:渲染组件树,在一个简化的测试环境中渲染组件树并对它们的输出做断言检查。
技术栈
- Jest:JavaScript 测试框架。
- @testing-library/react:将 React 组件转化成 Dom 节点来测试,而不是渲染的 React 组件的实例,可以当做是 Enzyme 的替代。
- ts-jest:是一个 TypeScript 预处理器,它支持 Jest 的源映射,允许你使用 Jest 测试用 TypeScript 编写的项目。
- jest-html-reporter:一个生成测试报告的库。
项目配置
因为之前使用的是 react-app-rewired
作为 Cli 工具,它会对 Jest
的版本有所限制。所以,下面安装依赖时,有些库会锁住版本。
npm i -D ts-jest@24.1.0 @types/jest jest@24.9.0 jest-html-reporter @testing-library/react @babel/preset-env @babel/preset-react
为了使用安装的这些依赖,接下来需要修改下 package.json
,增加测试,以及提交之前的校验。
...
"scripts": {
+ "test": "jest --no-cache",
}
...
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix src/**/*.{ts,tsx}",
+ "jest --bail --coverage --findRelatedTests",
"git add ."
],
"*.{md,css,html,less}": [
"prettier --write",
"git add ."
]
}
最后,还需要给 Jest
增加一个配置文件:jest.config.js
module.exports = {
preset: 'ts-jest',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
globals: {
'ts-jest': {
babelConfig: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
},
testEnvironment: 'jsdom',
testMatch: [
'<rootDir>/src/**/__tests__/**/*.{ts,tsx,js,jsx,mjs}',
'<rootDir>/src/**/?(*.)(spec|test).{ts,tsx,js,jsx,mjs}',
],
reporters: [
'default',
[
'./node_modules/jest-html-reporter',
{
pageTitle: '测试报告',
},
],
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverage: true,
collectCoverageFrom: ['app/react/**/*.{ts,tsx}', '!app/react/__tests__/api/api-test-helpers.ts'],
};
编写测试用例
这里,我需要给之前添加的 EmptyLine
,增加测试用例。
empty-line/index.tsx
import './style/index.less';
import EmptyLine from './EmptyLine';
export default EmptyLine;
empty-line/EmptyLine.tsx
import React from 'react';
export interface IEmptyLineProps {
height?: number;
}
const EmptyLine = ({ height = 20 }: IEmptyLineProps) => {
return <div className="d-empty-line" style={{ height }} />;
};
export default EmptyLine;
empty-line/__test__/index.test.tsx
import React from 'react';
import { create, act } from 'react-test-renderer';
import EmptyLine from '../EmptyLine';
test('默认高度渲染正常', () => {
const emptyLine = create(<EmptyLine />).toJSON();
expect(emptyLine?.props.style).toEqual(
expect.objectContaining({
height: 20,
}),
);
});
test('自定义高度渲染正常', () => {
let emptyLine: any;
act(() => {
emptyLine = create(<EmptyLine height={30} />);
});
expect(emptyLine.toJSON()?.props.style).toEqual(
expect.objectContaining({
height: 30,
}),
);
});
下面,就是两个 EmptyLine
常见的使用场景,已经在我们的单元测试中都覆盖到了。
需要注意的是,这里的 EmptyLine.tsx
组件中并没有引入样式,而是在同级的 index.tsx
中引入了样式。因为,这里样式在测试的时候是不支持引入的,会出问题。
运行 npm run test
就可以看到结果:
当我们提交代码,commit
的时候,也会在根目录生成一个测试报告:
结束语
到这里,我们给组件库增加了测试的功能。使我们的组件库也变得更加有保障,高大上了起来。最后,在第三章中,我们会聊聊如何打包,并上传至 NPM
的事情,敬请期待吧。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。