- 苏格团队
- 做者:Dee
单元测试的好处:css
因为笔者开发的项目愈来愈大,公共组件的复用性高,故其稳定性尤其重要。所以,引入单元测试刻不容缓。html
单元测试的很差:node
在作项目单元测试前,笔者参考了网上的一些文章以及官方文档,最后选型为Jest + react-test-renderer + Enzyme。react
Jestgit
Jest 是 Facebook 出品的一个测试框架,相对其余测试框架,其一大特色就是就是集成了 Mocha,chai,jsdom,sinon等功能,内置了经常使用的测试工具,好比自带断言、测试覆盖率工具,实现了开箱即用。github
react-test-renderweb
配合react-test-render,Jest 可提供了快照测试功能。npm
首次运行快照测试,会产生一个可读的快照,再次测试时会经过比对快照文件和新产生的快照判断测试是否经过。json
Jest在执行的时候若是发现toMatchSnapshot方法,会在同级目录下生成一个__ snapshots__文件夹用来存放快照文件,之后每次测试的时候都会和第一次生成的快照进行比较。浏览器
Enzyme
React官方已经提供了一个测试工具库:react-dom/test-utils。可是用起来不够方便,因而有了一些第三方的封装库,好比Airbnb公司的Enzyme。其两大特色:
三种渲染方法
shallow:浅渲染,是对官方的Shallow Renderer的封装。将组件渲染成虚拟DOM对象,只会渲染第一层,子组件将不会被渲染出来,使得效率很是高。不须要DOM环境, 并可使用jQuery的方式访问组件的信息
render:静态渲染,它将React组件渲染成静态的HTML字符串,而后使用Cheerio这个库解析这段字符串,并返回一个Cheerio的实例对象,能够用来分析组件的html结构
mount:彻底渲染,它将组件渲染加载成一个真实的DOM节点,用来测试DOM API的交互和组件的生命周期。用到了jsdom来模拟浏览器环境
三种方法中,shallow和mount由于返回的是DOM对象,能够用simulate进行交互模拟,而render方法不能够。通常shallow方法就能够知足需求,若是须要对子组件进行判断,须要使用render,若是须要测试组件的生命周期,须要使用mount方法。
注意:enzyme还须要根据React的版本安装适配器,适配器对应表以下:
前面说了这么多,是时候上代码了。
目录
笔者在根目录新建一个unitTest目录,其目录结构为:
jest.config.js:jest配置文件
mocks:mock文件目录
components:项目的公共组件单元测试用例目录
components/__ snapshots __:运行单元测试时自动生成的快照存放目录
安装(因为笔者是react16版本,因此安装的适配器版本为enzyme-adapter-react-16)
npm install jest enzyme enzyme-adapter-react-16 react-test-renderer
配置
Jest支持直接在package.json文件写入配置,但笔者有轻微洁癖,喜欢把配置文件写到unitTest里面,方便查找以及阅读。
// package.json
{
"scripts": {
"jest": "jest --config ./unitTest/jest.config.js", // 单元测试
"jestupdate": "jest --config ./unitTest/jest.config.js --updateSnapshot" // 单元测试快照更新
"jestreport": "jest --config ./unitTest/jest.config.js --coverage" // 单元测试并生成覆盖率报告
}
}
复制代码
// jest.config.js
module.exports = {
testURL: 'http://localhost/',
setupFiles: [],
moduleFileExtensions: ['js', 'jsx'],
testPathIgnorePatterns: ['/node_modules/'],
testRegex: '.*\\.test\\.js$',
collectCoverage: false,
collectCoverageFrom: ['src/components/**/*.{js}'],
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'
}
};
复制代码
mock文件
// fileMock.js
module.exports = {};
复制代码
// styleMock.js
module.exports = {};
复制代码
编写单元测试
// button.test.js
import Button from '../../src/common/components/Button';
import renderer from 'react-test-renderer';
import React from 'react';
import { shallow, configure } from 'enzyme'; // shallow(浅渲染,只渲染父组件)
import Adapter from 'enzyme-adapter-react-16'; // 适应React-16
configure({ adapter: new Adapter() }); // 适应React-16,初始化
const props = {
text: '按钮测试用例',
type: 'white',
style: { marginTop: 15 },
size: 'big',
disabled: false,
height: 'middle',
isLock: true,
cname: 'hello',
onClick: () => {}
};
describe('test Button', () => {
it('button render correctly', () => {
const tree = renderer.create(<Button {...props} />).toJSON();// 生成快照
expect(tree).toMatchSnapshot(); // 匹配以前的快照
});
it('button has class', () => {
const item = shallow(<Button {...props} />); //浅渲染
expect(item.hasClass('hello')).toBe(true); // 断言有item有hello的className
});
});
复制代码
注意事项:
一、若是不配置testURL,会报错:localStorage is not available for opaque origins
二、本文档只讲述笔者的实践方案以供参考,关于Jest、enzyme的具体介绍、用法可参考