React測試框架之enzyme
簡介
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
常用函式
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.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
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物件的緣故,所以都是可以模擬互動的。