zoukankan      html  css  js  c++  java
  • 【前端单元测试入门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的Full DOM Rendering测试结果

    如上,在mount得到的结果中可以找到子组件的元素

    Enzyme 的使用之render

    而Enzyme还提供了一个不需要jsdom模拟环境解决子组件测试的方法:render
    Enzyme的render函数得到的结果被称为Static Rendered Markup,以下为官方的介绍

    enzyme's render function is used to render react components to static HTML and analyze the resulting HTML structure.
    render returns a wrapper very similar to the other renderers in enzyme, mount and shallow; however, render uses 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())
      })
    })
    

    测试结果为:

    render测试结果

    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测试结果

    render测试结果

    mount测试结果

    结果证明:
    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

  • 相关阅读:
    js··事件捕捉
    js中的Call()和apply()
    什么是变量提升?
    什么是作用域? 什么是作用域链?
    什么是原型链?
    js中this是什么?
    Js高级 事件冒泡
    Js高级 事件 对象
    Js高级 部分内容 面向对象
    工作期间的策划案总结(1)
  • 原文地址:https://www.cnblogs.com/vvjiang/p/8599980.html
Copyright © 2011-2022 走看看