简介
Enzyme 是由 Airbnb 开源的一个 React 的 JavaScript 测试工具, 使 React 组件的输出更加容易 extrapolate .Enzyme 的 API 和 jQuery 操作 DOM 一样灵活易用, 因为它使用的是 cheerio 库来解析虚拟 DOM, 而 cheerio 的目标则是做服务器端的 jQuery.Enzyme 兼容大多数断言库和测试框架, 如 chai,mocha,jasmine 等.
安装与配置
使用 enzyme 之前, 需要在项目中安装 enzyme 依赖, 安装的命令如下:
NPM install --save-dev enzyme
由于 React 项目需要依赖 React 的一些东西, 所以请确保以下模块已经安装.
NPM install --save react react-dom babel-preset-react
要完成渲染测试, 除了 enzyme 之外, 还需要 Enzyme Adapter 库的支持, 由于 React 版本的不同, Enzyme Adapter 的版本也不一样. 适配器和 React 的对应表如下:
Enzyme Adapter Package | React semver compatibility |
---|---|
enzyme-adapter-react-16 | ^16.0.0 |
enzyme-adapter-react-15 | ^15.5.0 |
enzyme-adapter-react-14.4 | ^15.5.0 |
enzyme-adapter-react-14 | ^0.14.0 |
enzyme-adapter-react-13 | ^0.13.0 |
enzyme 支持三种方式的渲染: shallow: 浅渲染, 是对官方的 Shallow Renderer 的封装. 将组件渲染成虚拟 DOM 对象, 只会渲染第一层, 子组件将不会被渲染出来, 因而效率非常高. 不需要 DOM 环境, 并可以使用 jQuery 的方式访问组件的信息; render: 静态渲染, 它将 React 组件渲染成静态的 html 字符串, 然后使用 Cheerio 这个库解析这段字符串, 并返回一个 Cheerio 的实例对象, 可以用来分析组件的 HTML 结构. mount: 完全渲染, 它将组件渲染加载成一个真实的 DOM 节点, 用来测试 DOM API 的交互和组件的生命周期, 用到了 jsdom 来模拟浏览器环境.
常用函数
enzyme 中有几个比较核心的函数需要注意, 如下:
simulate(event, mock): 用来模拟事件触发, event 为事件名称, mock 为一个 event object;
instance(): 返回测试组件的实例;
find(selector): 根据选择器查找节点, selector 可以是 CSS 中的选择器, 也可以是组件的构造函数, 以及组件的 display name 等;
at(index): 返回一个渲染过的对象;
get(index): 返回一个 react node, 要测试它, 需要重新渲染;
contains(nodeOrNodes): 当前对象是否包含参数重点 node, 参数类型为 react 对象或对象数组;
text(): 返回当前组件的文本内容;
HTML(): 返回当前组件的 HTML 代码形式;
props(): 返回根组件的所有属性;
prop(key): 返回根组件的指定属性;
state(): 返回根组件的状态;
setState(nextState): 设置根组件的状态;
setProps(nextProps): 设置根组件的属性;
使用
为了方便讲解 Enzyme 测试的用法, 我们首先新建一个 enzyme.JS 的测试文件. 代码如下:
- import React from 'react'
- const Example=(props)=>{
- return (<div>
- <button>{props.text}</button>
- </div>)
- }
- export default Example
浅渲染 shallow
前面说过, Shallow Rendering 用于将一个组件渲染成虚拟 DOM 对象, 但是只渲染第一层, 不渲染所有子组件, 所以处理速度非常快. 并且它不需要 DOM 环境, 因为根本没有加载进 DOM.
为了进行浅渲染 shallow 测试, 我们新建一个名为 enzyme.test.JS 的测试文件.
- import React from 'react'
- import Enzyme from 'enzyme'
- import Adapter from 'enzyme-adapter-react-16'
- import Example from '../enzyme'
- const {shallow}=Enzyme
- Enzyme.configure({ adapter: new Adapter() })
- describe('Enzyme shallow', function () {
- it('Example component', function () {
- const name='按钮名'
- let App = shallow(<Example text={name} />)
- let btnName=App.find('button').text();
- console.log('button Name:'+btnName)
- })
- })
执行 yarn test 命令, 会看到如下的运行结果:
为了避免每个测试文件都这么写, 我们可以再 test 目录下新建一个配置文件 enzyme_config.test.JS. 文件内容如下:
- import Enzyme from 'enzyme';
- import Adapter from 'enzyme-adapter-react-16';
- Enzyme.configure({
- adapter: new Adapter(),
- });
- export default Enzyme;
然后, 在 test 目录下新建一个文件 setup.JS:
- import jsdom from 'jsdom';
- const {
- JSDOM
- } = jsdom;
- if (typeof document === 'undefined') {
- const dom=new JSDOM('<!doctype html><html><head></head><body></body></html>');
- global.Windows =dom.Windows;
- global.document = global.Windows.document;
- global.navigator = global.Windows.navigator;
- }
修改我们的 package.JSON 中的测试脚本为如下配置:
- "scripts": {
- "test": "mocha --require babel-core/register --require ./test/setup.js"
- }
现在, 我们的 shallow 测试代码可以改为:
- import React from 'react'
- import Enzyme from './enzyme.config';
- import Example from '../enzyme'
- const {shallow}=Enzyme
- describe('Enzyme shallow', function () {
- it('Example component', function () {
- const name='按钮名'
- let App = shallow(<Example text={name} />)
- let btnName= App.find('button').text()
- console.log('button Name:'+btnName)
- })
- })
完全渲染 mount
mount 渲染用于将 React 组件加载为真实 DOM 节点. 然而, 真实 DOM 需要一个浏览器环境, 为了解决这个问题, 我们可以用到 jsdom, 也就是说我们可以用 jsdom 模拟一个浏览器环境去加载真实的 DOM 节点. 首先, 使用下面的命令安装 jsdom 模拟浏览器环境, 安装命令如下:
NPM install --save-dev jsdom
然后我们添加一个完全渲染的测试代码:
- import React from 'react'
- import Enzyme from 'enzyme'
- import Adapter from 'enzyme-adapter-react-16'
- import Example from '../src/example'
- const {shallow,mount}=Enzyme
- Enzyme.configure({ adapter: new Adapter() })
- describe('Enzyme mount 的 DOM 渲染 (Full DOM Rendering) 中', function () {
- it('Example 组件中按钮的名字为子组件 Sub 中 span 的值', function () {
- const name='按钮名'
- let App = mount(<Example text={name} />)
- const buttonObj=App.find('button')
- const spanObj=App.find('span')
- console.info(` 查找到 button 的个数:${buttonObj.length}`)
- console.info(` 查找到 span 的个数:${spanObj.length}`)
- buttonObj.text(),spanObj.text()
- })
- })
说明, 由于完成测试的配置好像有点问题, 所以此处的代码有些跑不通.
静态渲染 render
render 静态渲染, 主要用于将 React 组件渲染成静态的 HTML 字符串, 然后使用 Cheerio 这个库解析这段字符串, 并返回一个 Cheerio 的实例对象, 可以用来分析组件的 HTML 结构. 针对前面的 enzyme.JS 文件, 我们的静态渲染测试的代码如下:
- import React from 'react'
- import Enzyme from 'enzyme'
- import Adapter from 'enzyme-adapter-react-16'
- import Example from '../enzyme'
- const {shallow,mount,render}=Enzyme
- Enzyme.configure({ adapter: new Adapter() })
- describe('Enzyme render test', function () {
- it('Example render', function () {
- const name='按钮名'
- let App = render(<Example text={name} />)
- const buttonObj=App.find('button')
- const spanObj=App.find('span')
- console.info(` 查找到 button 的个数:${buttonObj.length}`)
- console.info(` 查找到 span 的个数:${spanObj.length}`)
- buttonObj.text(),spanObj.text()
- })
- })
执行上面的代码, 测试结果如下:
对比
为了对比这三大测试框架, 我们可以对比看一下:
- describe('shallow vs render vs mount', function () {
- it('测试 shallow 500 次', () => {
- for (let i = 0; i <500; i++) {
- const App = shallow(<Example/>)
- App.find('button').text()
- }
- })
- it('测试 render500 次', () => {
- for (let i = 0; i <500; i++) {
- const App = render(<Example/>)
- App.find('button').text()
- }
- })
- it('测试 mount500 次', () => {
- for (let i = 0; i <500; i++) {
- const App = mount(<Example/>)
- App.find('button').text()
- }
- })
- })
运行结果如下图:
如上图, shallow 是最快的, 这是因为 shallow 的局限性, 只渲染第一层, 不渲染所有子组件. 事实证明, render 的效率是 mount 的两倍. 那么问题来了, mount 存在的价值是什么? 当然是有价值的, shallow 和 mount 因为都是 dom 对象的缘故, 所以都是可以模拟交互的.
来源: https://juejin.im/post/5be2c1bce51d45776654a090