10

Enzyme学习记录

 3 years ago
source link: https://www.ruphi.cn/archives/410/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Enzyme是由Airbnb所推出的React测试工具,它模拟了jQueryAPI,提供了一系列简洁易用的功能,这将使得我们进行React组件测试时非常地得心应手。

一、渲染形式

在开始介绍Enzyme之前,我们需要先了解一下React中的组件渲染形式以及React官方提供的测试方法。在React中,组件要么是Virtual DOM对象,要么是真实DOM节点。前者是React.Component的实例,后者则是renderpatchDOM中的实际结构。对于这两种渲染形式,React官方提供了测试的方法:

1、Shallow Rendering

这种渲染形式会将一个组件渲染为VDOM对象,但是只渲染第一层而不渲染所有的子组件,从而处理速度非常快。此外,由于没有渲染进DOM,所以这种渲染形式不需要DOM环境。它的使用示例如下:

// 被测试的组件
function TitleBlock(props) {
    const { title = '' } = props
    return (
        <div className="titleBlock">
            <h3 className="title">{title}</h3>
            <div className="contents">
                {props.children}
            </div>
        </div>
    )
}

测试脚本:

// react-addon-test-units 是官方提供的测试包
import TestUtils from 'react-addon-test-units'

function shallowRender(Component, props = {}) {
    const renderer = TestUtils.createRenderer()
    renderer.render(<Component {...props} />)
    return renderer.getRenderOutput()
}

// 测试用例
describe('Shallow Rendering', () => {
    it('Title should be correctly rendered', () => {
        const comp = shallowRender(TitleBlock, {
            title: 'HelloWorld'
        })
        expect(comp.props.children[0].props.children)
        .to
        .equal('HelloWorld')
    })
})

2、DOM Rendering

官方测试工具也提供了渲染为真实DOM的方法,主要是通过renderIntoDocument这个方法来提供的,示例如下:

import TestUtils from 'react-addon-test-units'
import TitleBlock from 'path/to/TitleBlock'

const comp = TestUtils.renderIntoDocument(<TitleBlock />)

这里会要求方法存在于真实的DOM环境中,但是通过Node跑测试时,我们怎么构造这个DOM环境呢?一个办法就是使用jsdom,如下:

import jsdom from 'jsdom'

if (typeof document === 'undefined') {
    global.document = jsdom.jsdom(/* 这里是一段HTML文本 */`
        <!doctype html>
        <html>
            <body></body>
        </html>
    `)
    global.window = global.document.defaultView
    global.navigator = global.window.navigator
}

因此,通过模拟了documentwindownavigator,我们成功构造了一个DOM环境。此外,在ReactTestUtils中存在了一系列用于DOM Rendering的方法,我们需要先了解它们:

  • scryRenderedDOMComponentsWithClass,找出所有匹配相应className的节点
  • findRenderedDOMComponentWithClass,找出匹配相应className的节点(只返回一个节点,若0或多个匹配会报错)
  • scryRenderedDOMComponentsWithTag,找出所有匹配指定标签的节点
  • findRenderedDOMComponentWithTag,找出所有匹配指定标签的节点(只返回一个节点,若0或多个匹配会报错)
  • scryRenderedComponentsWithType,找出所有符合指定类型的节点
  • findRenderedComponentWithType,找出所有符合指定类型的节点(只返回一个节点,若0或多个匹配会报错)
  • findAllInRenderedTree,遍历当前组件所有的节点,只返回那些符合条件的节点

接下来,我们就可以写测试用例了,如下:

// ... jsdom引入的代码 ...

describe('DOM Rendering', () => {
    it('Title should be correctly rendered', () => {
        const comp = shallowRender(TitleBlock, {
            title: 'HelloWorld'
        })
        const titleNodes = TestUtils.scryRenderedDOMComponentsWithTag(comp, 'h3')
        expect(titleNodes[0].innerText)
        .to
        .equal('HelloWorld')
    })
})

不过,对DOM结构的获取和处理,使用TestUtils这一套还是太麻烦了(名字又臭又长),我们其实还可以使用findDOMNode,这个方法接收选择器信息作为参数,并返回符合选择器的节点,用法如下:

import { findDOMNode } from 'react-dom'

// ... jsdom引入的代码 ...

describe('DOM Rendering', () => {
    it('Title should be correctly rendered', () => {
        const comp = shallowRender(TitleBlock, {
            title: 'HelloWorld'
        })
        const compDOM = findDOMNode(comp)
        const titleNode = compDOM.querySelector('h3')
        expect(titleElems[0].innerText)
        .to
        .equal('HelloWorld')
    })
})

二、Enzyme

以上,我们已经知道了如何用React官方的方式测试React组件,而现在则引出本文的重头戏——Enzyme

1、三种测试方法

Enzyme提供三种测试方法,分别是:

  • shallow
  • render
  • mount

其中,shallow方式就是对官方shallow rendering的封装,采用Enzyme后,我们可以改写为:

import { shallow } from 'enzyme'
import TitleBlock from 'path/to/TitleBlock'

describe('Enzyme: shallow', () => {
    it('Title should be correctly rendered', () => {
        const comp = shallow(<TitleBlock />)
        expect(comp.find('h3').at(0).text()).to.equal('HelloWorld')
    })
})

render方式则是将组件渲染为HTML字符串,然后分析这个字符串并返回一个对象,它与shallow方法很像,但是使用的是HTML解析库Cheerio。所以返回的是Cheerio的实例对象(而不是Enzyme实例对象),示例如下:

import { render } from 'enzyme'
import TitleBlock from 'path/to/TitleBlock'

describe('Enzyme: shallow', () => {
    it('Title should be correctly rendered', () => {
        const comp = render(<TitleBlock />)
        expect(comp.find('h3').at(0).text()).to.equal('HelloWorld')
    })
})

最后的这个mount,则是将组件渲染为真实的DOM节点(并且无需引入jsdom),示例如下:

import { mount } from 'enzyme'
import TitleBlock from 'path/to/TitleBlock'

describe('Enzyme: mount', () => {
    it('Title should be correctly rendered', () => {
        const comp = mount(<TitleBlock />)
        expect(comp.find('h3').at(0).text()).to.equal('HelloWorld')
    })
})

2、API

下面是常用的Enzyme API

  • find(selector),返回一个对象,该对象包含了所有符合选择器条件的子组件。不过只支持简单选择器,即:
    • 类选择器,如:.name
    • ID选择器,如:#name
    • 标签选择器,如:div
    • 组合选择器,如:div.name
    • 组件名引用,如:TitleBlock
  • get(idx),返回指定索引位置的子DOM节点
  • at(idx),返回指定索引位置的子组件
  • first(),返回第一个子组件
  • last(),返回最后一个子组件
  • type(),返回当前组件的类型
  • text(),返回当前组件的文本内容
  • html(),返回当前组件的HTML文本
  • props,返回根组件的所有属性
  • prop(key),名为${key}的属性
  • state([key]),返回根组件的状态
  • setState(nextState),设置根组件的状态
  • setProps(nextProps),设置根组件的属性
  • simulate(EventName),模拟事件

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK