Enzyme是由Airbnb開源的一個React的JavaScript測試工具,使React組件的輸出更加容易extrapolate 。Enzyme的API和jQuery操做DOM同樣靈活易用,由於它使用的是cheerio庫來解析虛擬DOM,而cheerio的目標則是作服務器端的jQuery。Enzyme兼容大多數斷言庫和測試框架,如chai、mocha、jasmine等。html
使用enzyme以前,須要在項目中安裝enzyme依賴,安裝的命令以下:node
npm install --save-dev enzyme
複製代碼
因爲React 項目須要依賴React的一些東西,因此請確保如下模塊已經安裝。react
npm install --save react react-dom babel-preset-react
複製代碼
要完成渲染測試,除了enzyme以外,還須要Enzyme Adapter庫的支持,因爲React 版本的不一樣,Enzyme Adapter的版本也不同。適配器和React的對應表以下:npm
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來模擬瀏覽器環境。json
enzyme中有幾個比較核心的函數須要注意,以下:數組
爲了方便講解Enzyme測試的用法,咱們首先新建一個enzyme.js的測試文件。代碼以下:瀏覽器
import React from 'react'
const Example=(props)=>{
return (<div>
<button>{props.text}</button>
</div>)
}
export default Example
複製代碼
前面說過,Shallow Rendering用於將一個組件渲染成虛擬DOM對象,可是隻渲染第一層,不渲染全部子組件,因此處理速度很是快。而且它不須要DOM環境,由於根本沒有加載進DOM。bash
爲了進行淺渲染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命令,會看到以下的運行結果: babel
爲了不每一個測試文件都這麼寫,咱們能夠再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.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"
}
複製代碼
如今,咱們的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渲染用於將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靜態渲染,主要用於將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對象的緣故,因此都是能夠模擬交互的。