【前端单元测试入门02】react的单元测试之Enzyme
React项目的单元测试
React的组件结构和JSX语法,对上一章的内容来讲进行测试显得很勉强。
React官方已经提供了一个测试工具库:react-dom/test-utils
只是用起来不够方便,于是有了一些第三方的封装库,比如Airbnb公司的Enzyme
测试项目的配置
本次测试项目是根据上一章的测试项目衍生而来,包含上一章讲到的Mocha和chai,这里只介绍新加的一些模块。
项目结构图如下:

因为是React项目,所以自然需要安装React的一些东西:
npm install --save react react-dom babel-preset-react
然后.babelrc文件内容改为
{
"presets": [ "es2015","react" ]
}
example.jsx的内容为:
import React from 'react'
const Example=(props)=>{
return (<div>
<button>{props.text}</button>
</div>)
}
export default Example
example.test.js的内容暂时为空
Enzyme 的安装与配置
npm install --save-dev enzyme
而enzyme还需要根据React的版本安装适配器,适配器对应表如下:
| Enzyme Adapter Package | React semver compatibility |
|---|---|
| enzyme-adapter-react-16 | ^16.0.0 |
| enzyme-adapter-react-15 | ^15.5.0 |
| enzyme-adapter-react-15.4 | 15.0.0-0 - 15.4.x |
| enzyme-adapter-react-14 | ^0.14.0 |
| enzyme-adapter-react-13 | ^0.13.0 |
那么因为我们安装的React版本为^16.2.0
所以需要安装:
npm install --save-dev enzyme-adapter-react-16
Enzyme 的使用
现在开始用Enzyme为example.jsx编写测试代码:
import {assert} from 'chai'
import React from 'react'
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import Example from '../src/example'
const {shallow}=Enzyme
Enzyme.configure({ adapter: new Adapter() })
describe('Enzyme的浅渲染测试套件', function () {
it('Example组件中按钮的名字为text的值', function () {
const name='按钮名'
let app = shallow(<Example text={name} />)
assert.equal(app.find('button').text(),name)
})
})
如上面代码所示,在使用Enzyme 前需要先适配React对应的版本
Enzyme.configure({ adapter: new Adapter() })
而为了避免每个测试文件都这么写,可以再test目录下新建一个配置文件:
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({
adapter: new Adapter(),
});
export default Enzyme;
然后测试文件的时候只需要引用这个配置文件即可:
import {assert} from 'chai'
import React from 'react'
import Enzyme from './config/Enzyme.config';
import Example from '../src/example'
const {shallow}=Enzyme
describe('Enzyme的浅渲染测试套件', function () {
it('Example组件中按钮的名字为text的值', function () {
const name='按钮名'
let app = shallow(<Example text={name} />)
assert.equal(app.find('button').text(),name)
})
})
Enzyme 的使用之浅渲染shallow
上面的例子中就用到浅渲染shallow 。
Shallow Rendering(浅渲染)指的是,将一个组件渲染成虚拟DOM对象,但是只渲染第一层,不渲染所有子组件,所以处理速度非常快。它不需要DOM环境,因为根本没有加载进DOM。
shallow的函数输入组件,返回组件的浅渲染结果,而返回的结果可以用类似jquery的形式获取组件的信息。
运行
npm test
也就是:
mocha --require babel-core/register
得到以下结果:

浅渲染测试与子组件的相关的代码:
现在修改我们的例子,新加一个sub.jsx:
import React from 'react'
const Sub=(props)=>{
return (<span>{props.text}</span>)
}
export default Sub
在原来的example.jsx中使用Sub,形成嵌套:
import React from 'react'
import Sub from './sub'
const Example=(props)=>{
return (<div>
<button>{props.text}</button>
<Sub text={props.text} />
</div>)
}
export default Example
使用shadow测试子组件的代码:
import {assert} from 'chai'
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 shallow的浅渲染(Shallow Rendering)中', function () {
it('Example组件中按钮的名字为子组件Sub中span的值', function () {
const name='按钮名'
let app = shallow(<Example text={name} />)
const buttonObj=app.find('button')
const spanObj=app.find('span')
console.info(`查找到button的个数:${buttonObj.length}`)
console.info(`查找到span的个数:${spanObj.length}`)
assert.equal(buttonObj.text(),spanObj.text())
})
})
测试结果为:

如上图所示,shallow所得到的浅渲染对象中差找不到子组件Sub的span元素。
为了解决上面这种情况,Enzyme给出的解决方案为用mount实现 Full DOM Rendering。
Enzyme 的使用之mount
mount方法用于将React组件加载为真实DOM节点。
然而真实DOM需要一个浏览器环境,为了解决这个问题,我们可以用到jsdom.
下面是jsdom的官方介绍:
jsdom is a pure-JavaScript implementation of many web standards, notably the WHATWG DOM and HTML Standards, for use with Node.js. In general, the goal of the project is to emulate enough of a subset of a web browser to be useful for testing and scraping real-world web applications.
也就是说我们可以用jsdom模拟一个浏览器环境去加载真实的DOM节点。
首先安装jsdom:
npm install --save-dev jsdom
然后在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.window =dom.window;
global.document = global.window.document;
global.navigator = global.window.navigator;
}
最后修改我们的package.json中的测试脚本为:
"scripts": {
"test": "mocha --require babel-core/register --require ./test/setup.js"
}
这样在运行npm test时,会先用babel解析js,然后再执行setup.js中的代码,给global对象模拟一个浏览器环境。
在配置好模拟浏览器环境后,修改测试代码为:
import {assert} from 'chai'
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}`)
assert.equal(buttonObj.text(),spanObj.text())
})
})
运行
npm test
结果为:

如上,在mount得到的结果中可以找到子组件的元素
Enzyme 的使用之render
而Enzyme还提供了一个不需要jsdom模拟环境解决子组件测试的方法:render。
Enzyme的render函数得到的结果被称为Static Rendered Markup,以下为官方的介绍
enzyme's
renderfunction is used to render react components to static HTML and analyze the resulting HTML structure.
renderreturns a wrapper very similar to the other renderers in enzyme,mountandshallow; however,renderuses a third party HTML parsing and traversal library Cheerio. We believe that Cheerio handles parsing and traversing HTML extremely well, and duplicating this functionality ourselves would be a disservice.
意思就是说render会根据react组件得到一个静态HTML文本结果,借助一个第三方的HTML解析库Cheerio去生成一个类似于mount和shallow得到的封装对象。
修改测试代码为:
import {assert} from 'chai'
import React from 'react'
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import Example from '../src/example'
const {shallow,mount,render}=Enzyme
Enzyme.configure({ adapter: new Adapter() })
describe('Enzyme render的静态HTML渲染(Static Rendered Markup)中', function () {
it('Example组件中按钮的名字为子组件Sub中span的值', 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}`)
assert.equal(buttonObj.text(),spanObj.text())
})
})
测试结果为:

shallow ,render和mount的效率对比
空口无凭,直接上测试代码:
it('测试 shallow 500次', () => {
for (let i = 0; i < 500; i++) {
const nav = shallow(<Nav />)
assert.equal(nav.find('a').text(), '首页')
}
})
it('测试render500次', () => {
for (let i = 0; i < 500; i++) {
const nav = render(<Nav />)
assert.equal(nav.find('a').text(), '首页')
}
})
it('测试mount500次', () => {
for (let i = 0; i < 500; i++) {
const nav = mount(<Nav />)
assert.equal(nav.find('a').text(), '首页')
}
})
结果:



结果证明:
shallow果然最快,这是肯定的,但是因为shallow的局限性,我们可能更想知道render和mount的效率。
事实证明,render的效率是mount的两倍。
有人可能要质疑你为什么不将次数弄更大一点,因为在设定为1000次的情况下mount直接超时了,也就是超过了mocha的默认执行时间限定2000ms。
那么问题来了,mount存在的价值是什么,render就可以测试子组件,render还不需要jsdom和额外的配置。
当然是有价值的,shallow和mount因为都是dom对象的缘故,所以都是可以模拟交互的,比如
const nav = mount(<Nav />)
nav.find('a').simulate('click')
而render是不能的。
小结
简而言之,Enzyme主要包括三个测试:
一个是浅渲染的shallow,这个生成虚DOM对象,所以渲染最快,然而它并不能测试子组件的相关代码。
另一个是DOM渲染mount,它会生成完整的DOM节点,所以可以测试子组件。但是要依赖一个用jsdom模拟的浏览器环境。
最后一个是HTML文本渲染render,它会将react组件渲染为html文本,然后在内部通过Cheerio自动生成一个Cheerio对象。
| 渲染方法 | 是否可以测试子组件 | 是否可以模拟交互 | 性能(测试500次) |
|---|---|---|---|
| shallow | 否 | 是 | 116ms |
| mount | 是 | 是 | 421ms |
| render | 是 | 否 | 984ms |
文中介绍了简单的用法,具体的API文档见:
Shallow Rendering
Full DOM Rendering
Static Rendered Markup
【前端单元测试入门02】react的单元测试之Enzyme的更多相关文章
- 【前端单元测试入门05】react的单元测试之jest
jest jest是facebook推出的一款测试框架,集成了前面所讲的Mocha和chai,jsdom,sinon等功能. 安装 npm install --save-dev jest npm in ...
- 【前端单元测试入门04】Karma
Karma 官方介绍 A simple tool that allows you to execute JavaScript code in multiple real browsers. 即一个允许 ...
- 【前端单元测试入门03】Sinon
前端测试存在的问题 在讲Sinon之前,我们得先讲一下在学习了Mocha.chai以及enzyme之后,我们的前端测试还存在的一些问题. 比如前台测试需要与后台交互,获取后台数据后再根据相应数据进行测 ...
- 【前端单元测试入门01】Mocha与chai
Mocha 的简介 Mocha是流行的JavaScript测试框架之一,通过它添加和运行测试,从而保证代码质量 Mocha 的安装与配置 全局安装Mocha npm install -g mocha ...
- Visual Studio 单元测试之二---顺序单元测试
原文:Visual Studio 单元测试之二---顺序单元测试 此文是上一篇博文:Visual Studio 单元测试之一---普通单元测试的后续篇章.如果读者对Visual Studio的单元测试 ...
- VS2010单元测试入门实践教程
单元测试的重要性这里我就不多说了,以前大家一直使用NUnit来进行单元测试,其实早在Visual Studio 2005里面,微软就已经集成了一个叫Test的专门测试插件,经过几年的发展,这个工具现在 ...
- 前端迷思与React.js
前端迷思与React.js 前端技术这几年蓬勃发展, 这是当时某几个项目需要做前端技术选型时, 相关资料整理, 部分评论引用自社区. 开始吧: 目前, Web 开发技术框架选型为两种的占 80% .这 ...
- 单元测试之NUnit一
NUnit 分三篇文章介绍,入门者可阅读文章,有基础者直接参考官方文档.初次写博客,望大家指点. 导航: 单元测试之NUnit一 单元测试之NUnit二 单元测试之NUnit三 NUnit是什么? N ...
- ASP.NET Core搭建多层网站架构【12-xUnit单元测试之集成测试】
2020/02/01, ASP.NET Core 3.1, VS2019, xunit 2.4.1, Microsoft.AspNetCore.TestHost 3.1.1 摘要:基于ASP.NET ...
随机推荐
- 1.1 Python是一种什么样的语言
小时不识月,呼作白玉盘.很多人习惯地说Python不过是一种脚本语言而已,实际上这种说法是非常不准确的,完全不能体现出Python的强大.严格来说,Python是一门跨平台.开源.免费的解释型高级动态 ...
- nyoj940 A dp problem 打表
首先枚举i,那么构成i^2的最小值为1个正方形,当然1~1000并不会都得到答案,那么剩下的数字就递增枚举这些数,这个数可能右多对数构成,则枚举这些数.例如 5 = 1 + 4, 5 = 2 + 3, ...
- asp.net程序发布详解
本文转载自Alan_beijing的博客ASP.NET 程序发布详细过程.内容进行了部分更改. ASP.NET网站的发布,无论是初学者还是高手,在程序的发布过程中或多或少会存在一些问题,譬如VS发布A ...
- dwr3+spring实现消息实时推送
最近项目要实现一个消息推送的功能,主要就是发送站内信或者系统主动推送消息给当前在线的用户.每次的消息内容保存数据库,方便用户下次登录后也能看到.如果当前用户在线,收到站内信就主动弹出提示.一开始想到的 ...
- MyEclipse中如何去掉JS/JSP语法错误提示
一 优化一下MyEclipse 1 .关闭MyEclipse的自动validation windows > perferences > myeclipse > val ...
- 关于HC04超声波模块测距的思考(51版)
之前写过一篇HC04的使用文章,当时是使用stm32来实现的,原文链接. 后来又多次使用51来驱动这个模块,有时候有测距需要,使用了几次,总是感觉我上次那个程序不是很好, 所以这次对它进行了改进.虽然 ...
- Windows2003查看远程桌面连接的用户
要查看通过远程连接windows2003的用户,则打开任务管理器,切换到“用户”选项卡上进行查看.
- MyEclipse报错
MyEclipse报错
- AM335x(TQ335x)学习笔记——触摸屏驱动编写
前面几篇文章已经通过配置DTS的方式完成了多个驱动的移植,接下来我们解决TQ335x的触摸驱动问题.由于种种原因,TQ335x的触摸屏驱动是以模块方式提供的,且Linux官方内核中也没有带该触摸屏的驱 ...
- FAT32文件系统的存储组织结构(二)
前面已经基于一个格式化的空U盘分析了一下FAT32文件系统存储的组织结构,下面我们从文件操作的角度来分析一下文件系统的运作机制.由于换了个U盘,所以仍然贴出刚格式化的空U盘的几个重要的数据区如下: ...