基于 Jest + Enzyme 的 React 单元测试
前言
如果你想学习 React 单元测试,那就从这篇文章开始吧。Star 项目,clone 到本地,根据教程走一遍,有任何问题欢迎 issue 讨论。
项目GitHub地址:react-test-demo
文章主要内容如下:
- Jest 和 Enzyme 的基本介绍
- 测试环境搭建
- 测试脚本编写
- UI 组件测试
- Reducer 测试
- 运行并调试
- 参考资料
Jest、Enzyme 介绍
Jest 是 Facebook 发布的一个开源的、基于 Jasmine
框架的 JavaScript 单元测试工具。提供了包括内置的测试环境 DOM API 支持、断言库、Mock 库等,还包含了 Spapshot Testing、 Instant Feedback 等特性。
Airbnb开源的 React 测试类库 Enzyme 提供了一套简洁强大的 API,并通过 jQuery 风格的方式进行DOM 处理,开发体验十分友好。不仅在开源社区有超高人气,同时也获得了React 官方的推荐。
测试环境搭建
在开发 React 应用的基础上(默认你用的是 Webpack + Babel 来打包构建应用),你需要安装 Jest
Enzyme
,以及对应的 babel-jest
1
npm install jest enzyme babel-jest --save-dev
下载 npm 依赖包之后,你需要在 package.json
中新增属性,配置 Jest:
1 | "jest": { |
并新增test scripts
1
2
3
4"scripts": {
"dev": "NODE_ENV=development webpack-dev-server --inline --progress --colors --port 3000 --host 0.0.0.0 ",
"test": "jest"
}
其中 :
moduleFileExtensions
:代表支持加载的文件名,与 Webpack 中的resolve.extensions
类似moduleNameMapper
:代表需要被 Mock 的资源名称。如果需要 Mock 静态资源(如less、scss等),则需要配置 Mock 的路径<rootDir>/__mocks__/yourMock.js
transform
用于编译 ES6/ES7 语法,需配合babel-jest
使用
上面三个是常用的配置,更多 Jest 配置见官方文档:Jest Configuration
测试脚本编写
UI 组件测试
环境搭建好了,就可以开始动手写测试脚本了。在开始之前,先分析下 Todo 应用的组成部分。
应用主体结构如下 src/component/App.js
:1
2
3
4
5
6
7
8
9
10
11
12
13
14class App extends Component {
render() {
const { params } = this.props;
return (
<section className="todoapp">
<div className="main">
<AddTodo />
<VisibleTodoList filter={params.filter || 'all'} />
</div>
<Footer />
</section>
)
}
}
可以发现 整个应用可以分为三个组件:
- 最外层的
<App />
- 中间的 Input 输入框
<AddTodo />
- 下面的 TODO 列表
<VisibleTodoList />
其中 <App/>
是 UI 组件,<AddTodo />
和 <VisibleTodoList />
是智能组件,我们需要找到智能组件所对应的 UI 组件 <AddTodoView/>
和 <TodoList/>
。
<AddTodoView/>
就是一个 Input
输入框,接受文字输入,敲下回车键,创建一个 Todo。代码如下 src/component/AddTodoView.js
:
1 | import React, { Component, PropTypes } from 'react' |
了解了该组件的功能之后,我们首先需要明确该组件需要测试哪些点:
- 组件是否正常渲染
- 当用户输入内容敲下回车键时,是否能正常的调用
props
传递的onAddClick(text)
方法 - 创建完成后清除 Input 的值
- 当用户没有输入任何值时,敲下回车时,应该不调用
props
传递的onAddClick(text)
方法
经过上面的分析之后,我们就可以开始编写单元测试脚本了。
第一步:引入相关 lib
1 | import React from 'react' |
在这里我们引入了 shallow
方法,它是 Enzyme
提供的 API 之一,可以实现浅渲染。其作用是仅仅渲染至虚拟节点,不会返回真实的节点,能极大提高测试性能。但是它不适合测试包含子组件、需要测试声明周期的组件。Enzyme
还提供了其他两个 API:
mount
:Full Rendering,非常适用于存在于 DOM API 存在交互组件,或者需要测试组件完整的声明周期render
:Static Rendering,用于 将 React 组件渲染成静态的 HTML 并分析生成的 HTML 结构。render
返回的wrapper
与其他两个 API 类似。不同的是render
使用了第三方 HTML 解析器和Cheerio
。
一般情况下,shallow
就已经足够用了,偶尔情况下会用到 mount
。
第二步:模拟 Props,渲染组件创建 Wrapper
这一步,我们可以创建一个 setup
函数来实现。
1 | const setup = () => { |
Props
中包含函数的时候,我们需要使用 Jest 提供的 mockFunction
第四步:编写 Test Case
这里的 Case 根据我们前面分析需要测试的点编写。
Case1:测试组件是否正常渲染
1 | describe('AddTodoView', () => { |
写完第一个测试用例之后,我们可以运行看看测试的效果。在 Terminal 中输入 npm run test
,效果如下:
Case2: 输入内容并敲下回车键,测试组件调用props的方法
1 | it('When the Enter key was pressed, onAddClick() shoule be called', () => { |
上面的代码与第一个 case 多了两点:
- 增加了
mockEvent
,用于模拟 DOM 事件 - 使用
Enzyme
提供的.simulate(’keyup‘, mockEvent)
来模拟点击事件,这里的keyup
会自动转换成 React 组件中的onKeyUp
并调用。
我们再运行 npm run test
看看测试效果:
经过上面两个 Test Case 的分析,接下来的 Case3 和 Case4 思路也是一样,具体写法见代码: test/component/AddTodoView.spec.js,这里就不一一讲解了。
Reducer 测试
由于 Reducer 是纯函数,因此对 Reducer 的测试非常简单,Redux 官方文档也提供了测试的例子,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62import reducer from '../../reducers/todos'
import * as types from '../../constants/ActionTypes'
describe('todos reducer', () => {
it('should return the initial state', () => {
expect(
reducer(undefined, {})
).toEqual([
{
text: 'Use Redux',
completed: false,
id: 0
}
])
})
it('should handle ADD_TODO', () => {
expect(
reducer([], {
type: types.ADD_TODO,
text: 'Run the tests'
})
).toEqual(
[
{
text: 'Run the tests',
completed: false,
id: 0
}
]
)
expect(
reducer(
[
{
text: 'Use Redux',
completed: false,
id: 0
}
],
{
type: types.ADD_TODO,
text: 'Run the tests'
}
)
).toEqual(
[
{
text: 'Run the tests',
completed: false,
id: 1
},
{
text: 'Use Redux',
completed: false,
id: 0
}
]
)
})
})
更多关于 Redux 的测试可以看官网提供的例子:编写测试-Redux文档
调试及测试覆盖率报告
在运行测试脚本过程,Jest
的错误提示信息友好,通过错误信息一般都能找到问题的所在。
同时 Jest
还提供了生成测试覆盖率报告的命令,只需要添加上 --coverage
这个参数既可生成。不仅会在终端中显示:
而且还会在项目中生成 coverage
文件夹,非常方便。