1. 程式人生 > >React測試框架之enzyme

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

:完全渲染,它將元件渲染載入成一個真實的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.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物件的緣故,所以都是可以模擬互動的。