若是你想学习 React 单元测试,那就从这篇文章开始吧。Star 项目,clone 到本地,根据教程走一遍,有任何问题欢迎 issue 讨论。javascript
项目GitHub地址:react-test-democss
文章主要内容以下:html
Jest 是 Facebook 发布的一个开源的、基于 Jasmine
框架的 JavaScript 单元测试工具。提供了包括内置的测试环境 DOM API 支持、断言库、Mock 库等,还包含了 Spapshot Testing、 Instant Feedback 等特性。前端
Airbnb开源的 React 测试类库 Enzyme 提供了一套简洁强大的 API,并经过 jQuery 风格的方式进行DOM 处理,开发体验十分友好。不只在开源社区有超高人气,同时也得到了React 官方的推荐。java
在开发 React 应用的基础上(默认你用的是 Webpack + Babel 来打包构建应用),你须要安装 Jest
Enzyme
,以及对应的 babel-jest
node
npm install jest enzyme babel-jest --save-dev
复制代码
下载 npm 依赖包以后,你须要在 package.json
中新增属性,配置 Jest:react
"jest": {
"moduleFileExtensions": [
"js",
"jsx"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
".*\\.(css|less|scss)$": "<rootDir>/__mocks__/styleMock.js"
},
"transform": {
"^.+\\.js$": "babel-jest"
}
},
复制代码
并新增test scripts
webpack
"scripts": {
"dev": "NODE_ENV=development webpack-dev-server --inline --progress --colors --port 3000 --host 0.0.0.0 ",
"test": "jest"
}
复制代码
其中 :git
moduleFileExtensions
:表明支持加载的文件名,与 Webpack 中的 resolve.extensions
相似moduleNameMapper
:表明须要被 Mock 的资源名称。若是须要 Mock 静态资源(如less、scss等),则须要配置 Mock 的路径 <rootDir>/__mocks__/yourMock.js
transform
用于编译 ES6/ES7 语法,需配合 babel-jest
使用上面三个是经常使用的配置,更多 Jest 配置见官方文档:Jest Configurationgithub
环境搭建好了,就能够开始动手写测试脚本了。在开始以前,先分析下 Todo 应用的组成部分。
应用主体结构以下 src/component/App.js
:
class 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 />
<AddTodo />
<VisibleTodoList />
其中 <App/>
是 UI 组件,<AddTodo />
和 <VisibleTodoList />
是智能组件,咱们须要找到智能组件所对应的 UI 组件 <AddTodoView/>
和 <TodoList/>
。
<AddTodoView/>
就是一个 Input
输入框,接受文字输入,敲下回车键,建立一个 Todo。代码以下 src/component/AddTodoView.js
:
import React, { Component, PropTypes } from 'react'
class AddTodoView extends Component {
render() {
return (
<header className="header"> <h1>todos</h1> <input className="new-todo" type="text" onKeyUp={e => this.handleClick(e)} placeholder="input todo item" ref='input' /> </header> ) } handleClick(e) { if (e.keyCode === 13) { const node = this.refs.input; const text = node.value.trim(); text && this.props.onAddClick(text); node.value = ''; } } } 复制代码
了解了该组件的功能以后,咱们首先须要明确该组件须要测试哪些点:
props
传递的 onAddClick(text)
方法props
传递的 onAddClick(text)
方法通过上面的分析以后,咱们就能够开始编写单元测试脚本了。
import React from 'react'
import App from '../../src/component/App'
import { shallow } from 'enzyme'
复制代码
在这里咱们引入了 shallow
方法,它是 Enzyme
提供的 API 之一,能够实现浅渲染。其做用是仅仅渲染至虚拟节点,不会返回真实的节点,能极大提升测试性能。可是它不适合测试包含子组件、须要测试声明周期的组件。 Enzyme
还提供了其余两个 API:
mount
:Full Rendering,很是适用于存在于 DOM API 存在交互组件,或者须要测试组件完整的声明周期render
:Static Rendering,用于 将 React 组件渲染成静态的 HTML 并分析生成的 HTML 结构。render
返回的 wrapper
与其余两个 API 相似。不一样的是 render
使用了第三方 HTML 解析器和 Cheerio
。通常状况下,shallow
就已经足够用了,偶尔状况下会用到 mount
。
这一步,咱们能够建立一个 setup
函数来实现。
const setup = () => {
// 模拟 props
const props = {
// Jest 提供的mock 函数
onAddClick: jest.fn()
}
// 经过 enzyme 提供的 shallow(浅渲染) 建立组件
const wrapper = shallow(<AddTodoView {...props} />) return { props, wrapper } } 复制代码
Props
中包含函数的时候,咱们须要使用 Jest 提供的 mockFunction
这里的 Case 根据咱们前面分析须要测试的点编写。
Case1:测试组件是否正常渲染
describe('AddTodoView', () => {
const { wrapper, props } = setup();
// case1
// 经过查找存在 Input,测试组件正常渲染
it('AddTodoView Component should be render', () => {
//.find(selector) 是 Enzyme shallow Rendering 提供的语法, 用于查找节点
// 详细用法见 Enzyme 文档 http://airbnb.io/enzyme/docs/api/shallow.html
expect(wrapper.find('input').exists());
})
})
复制代码
写完第一个测试用例以后,咱们能够运行看看测试的效果。在 Terminal 中输入 npm run test
,效果以下:
Case2: 输入内容并敲下回车键,测试组件调用props的方法
it('When the Enter key was pressed, onAddClick() shoule be called', () => {
// mock input 输入和 Enter事件
const mockEvent = {
keyCode: 13, // enter 事件
target: {
value: 'Test'
}
}
// 经过 Enzyme 提供的 simulate api 模拟 DOM 事件
wrapper.find('input').simulate('keyup',mockEvent)
// 判断 props.onAddClick 是否被调用
expect(props.onAddClick).toBeCalled()
})
复制代码
上面的代码与第一个 case 多了两点:
mockEvent
,用于模拟 DOM 事件Enzyme
提供的 .simulate(’keyup‘, mockEvent)
来模拟点击事件,这里的 keyup
会自动转换成 React 组件中的 onKeyUp
并调用。咱们再运行 npm run test
看看测试效果:
通过上面两个 Test Case 的分析,接下来的 Case3 和 Case4 思路也是同样,具体写法见代码: test/component/AddTodoView.spec.js,这里就不一一讲解了。
因为 Reducer 是纯函数,所以对 Reducer 的测试很是简单,Redux 官方文档也提供了测试的例子,代码以下:
import 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
文件夹,很是方便。
掘金技术征文第三期:聊聊你的最佳实践:https://juejin.im/post/58d8e99261ff4b006cd6874d