React单元测试实战

栏目: 编程工具 · 发布时间: 5年前

内容简介:由于笔者开发的项目越来越大,公共组件的复用性高,故其稳定性尤为重要。因此,引入单元测试刻不容缓。在做项目单元测试前,笔者参考了网上的一些文章以及官方文档,最后选型为Jest + react-test-renderer + Enzyme。前面说了这么多,是时候上代码了。

单元测试的好处:

  • 可保证得到结果的一致性,提高项目、组件稳定性。
  • 开发者按单元测试思路去写代码,可清晰代码结构,提高代码的可读性。

由于笔者开发的项目越来越大,公共组件的复用性高,故其稳定性尤为重要。因此,引入单元测试刻不容缓。

单元测试的不好:

  • 会占用一定的开发成本,增加开发工作量。
  • 旧项目加入单元测试改动很大,会有一定的风险。
  • 会有一定的学习成本,对开发者要求比较高。
  • 如果在一些复用性很低的组件使用单元测试,成效不大且开发成本高。

选型

在做项目单元测试前,笔者参考了网上的一些文章以及官方文档,最后选型为Jest + react-test-renderer + Enzyme。

  • Jest

    Jest 是 Facebook 出品的一个测试框架,相对其他测试框架,其一大特点就是就是集成了 Mocha,chai,jsdom,sinon等功能,内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。

  • react-test-render

    配合react-test-render,Jest 可提供了快照测试功能。

    首次运行快照测试,会产生一个可读的快照,再次测试时会通过比对快照文件和新产生的快照判断测试是否通过。

    Jest在执行的时候如果发现 toMatchSnapshot 方法,会在同级目录下生成一个__ snapshots__文件夹用来存放快照文件,以后每次测试的时候都会和第一次生成的快照进行比较。

  • Enzyme

    React官方已经提供了一个测试 工具 库:react-dom/test-utils。但是用起来不够方便,于是有了一些第三方的封装库,比如Airbnb公司的Enzyme。其两大特点:

    • 提供了一套简洁强大的 API,并内置Cheerio
    • 实现了jQuery风格的方式进行DOM 处理,开发体验十分友好

    三种渲染方法

    shallow:浅渲染,是对官方的Shallow Renderer的封装。将组件渲染成虚拟DOM对象,只会渲染第一层,子组件将不会被渲染出来,使得效率非常高。不需要DOM环境, 并可以使用jQuery的方式访问组件的信息

    render:静态渲染,它将React组件渲染成静态的HTML字符串,然后使用Cheerio这个库解析这段字符串,并返回一个Cheerio的实例对象,可以用来分析组件的html结构

    mount:完全渲染,它将组件渲染加载成一个真实的DOM节点,用来测试DOM API的交互和组件的生命周期。用到了jsdom来模拟浏览器环境

    三种方法中, shallowmount 因为返回的是DOM对象,可以用simulate进行交互模拟,而 render 方法不可以。一般 shallow 方法就可以满足需求,如果需要对子组件进行判断,需要使用 render ,如果需要测试组件的生命周期,需要使用 mount 方法。

    注意: enzyme 还需要根据React的版本安装适配器,适配器对应表如下:

React单元测试实战

方案

前面说了这么多,是时候上代码了。

  • 目录

    笔者在根目录新建一个 unitTest 目录,其目录结构为:

    React单元测试实战
    • jest.config.js:jest配置文件

    • mocks:mock文件目录

    • components:项目的公共组件单元测试用例目录

    • components/__ snapshots __:运行单元测试时自动生成的快照存放目录

  • 安装(由于笔者是react16版本,所以安装的适配器版本为enzyme-adapter-react-16)

    npm install jest enzyme enzyme-adapter-react-16 react-test-renderer

  • 配置

    Jest支持直接在package.json文件写入配置,但笔者有轻微洁癖,喜欢把配置文件写到 unitTest 里面,方便查找以及阅读。

    // package.json
    {
        "scripts": {
            "jest": "jest --config ./unitTest/jest.config.js", // 单元测试
            "jestupdate": "jest --config ./unitTest/jest.config.js --updateSnapshot" // 单元测试快照更新
            "jestreport": "jest --config ./unitTest/jest.config.js --coverage" // 单元测试并生成覆盖率报告
        }
    }
    复制代码
    // jest.config.js
    module.exports = {
        testURL: 'http://localhost/',
        setupFiles: [],
        moduleFileExtensions: ['js', 'jsx'],
        testPathIgnorePatterns: ['/node_modules/'],
        testRegex: '.*\\.test\\.js$',
        collectCoverage: false,
        collectCoverageFrom: ['src/components/**/*.{js}'],
        moduleNameMapper: {
            '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
                '<rootDir>/mocks/fileMock.js',
            '\\.(css|less|scss)$': '<rootDir>/mocks/styleMock.js'
        }
    };
    
    复制代码
    • testURL: jsdom运行url,默认为"about:blank",如果不设置,会在尝试访问localStorage出错。
    • setupFiles:运行测试代码前,Jest会先运行setupFile指定的配置文件来初始化测试环境。
    • moduleFileExtensions:支持单元测试的文件扩展名。
    • testPathIgnorePatterns:匹配忽略文件规则。
    • testRegex:匹配测试文件规则。
    • collectCoverage:是否生成测试覆盖报告,开启会增加测试时间。
    • collectCoverageFrom:指示应收集覆盖率信息的一组文件。如果文件与指定的glob模式匹配,即使此文件不存在测试,也将为其收集覆盖率信息,并且测试套件中从不需要它。
    • moduleNameMapper:可用于将模块路径映射到不同的模块。默认情况下,预设将所有图像映射到图像存根模块,但如果找不到模块,可配置此选项。
  • mock文件

    // fileMock.js
    module.exports = {};
    复制代码
    // styleMock.js
    module.exports = {};
    复制代码
  • 编写单元测试

    // button.test.js
    import Button from '../../src/common/components/Button';
    import renderer from 'react-test-renderer';
    import React from 'react';
    import { shallow, configure } from 'enzyme'; // shallow(浅渲染,只渲染父组件)
    import Adapter from 'enzyme-adapter-react-16'; // 适应React-16
    configure({ adapter: new Adapter() }); // 适应React-16,初始化
    const props = {
        text: '按钮测试用例',
        type: 'white',
        style: { marginTop: 15 },
        size: 'big',
        disabled: false,
        height: 'middle',
        isLock: true,
        cname: 'hello',
        onClick: () => {}
    };
    describe('test Button', () => {
        it('button render correctly', () => {
            const tree = renderer.create(<Button {...props} />).toJSON();// 生成快照
            expect(tree).toMatchSnapshot(); // 匹配之前的快照
        });
    
        it('button has class', () => {
            const item = shallow(<Button {...props} />); //浅渲染
            expect(item.hasClass('hello')).toBe(true); // 断言有item有hello的className
        });
    });
    
    
    复制代码

后记

注意事项:

1、如果不配置testURL,会报错:localStorage is not available for opaque origins

2、本文档只讲述笔者的实践方案以供参考,关于Jest、enzyme的具体介绍、用法可参考


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Persuasive Technology

Persuasive Technology

B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95

Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器