使用 Enzyme 进行 React 组件测试进阶

栏目: IOS · Android · 发布时间: 5年前

内容简介:很早之前,写过一篇项目使用安装 karma

很早之前,写过一篇 《使用enzyme 测试你的 React 组件》 , 综合叙述了如何利用 Karma + Webpack + Enzyme 进行组件的测试, 从而确保我们的质量。 相对而言,这次会丰富一些,比如组件的 UI 事件以及 redux 引入后的测试。

项目使用 yarn-react-webpack-seed 为案例,你可以在项目里找到源码。

建立测试

安装 karma

$ npm install karma karma-chai karma-chrome-launcher karma-coverage karma-jasmine karma-sourcemap-loader jasmine-core karma-webpack --save-dev

安装 enzyme 相关

npm install enzyme redux-mock-store enzyme-adapter-react-16 jasmine-enzyme --save-dev

在项目建立 test 目录,新增 karma.conf.js

然后在 package.json 的 script 添加

"test": "NODE_ENV=test karma start ./test/karma.conf.js --log-level debug"

接下里我们在 test 添加 specs 目录,用于存放我们的测试文件。

同时我们需要 test 添加 enzyme.js

import Enzyme from 'enzyme';  
import Adapter from 'enzyme-adapter-react-15.4';  
import jasmineEnzyme from 'jasmine-enzyme';

// Configure Enzyme for the appropriate React adapter
Enzyme.configure({ adapter: new Adapter() });

// Initialize global helpers
beforeEach(() => {  
  jasmineEnzyme();
});

// Re-export all enzyme exports
export * from 'enzyme';

接下来我们测试你一个简单的组件。

我们在 src/components 建立一个简单的 links.js 组件

import React, { Component } from 'react';  
import { Link } from 'react-router-dom';

class Links extends Component {

  render() {
    return (
      <div>
        <aside>
          <h4>Categories</h4>
          <ul>
            <li><Link to="/start">Start</Link></li>
            <li><Link to="/how">How</Link></li>
            <li><Link to="/guide">Guide</Link></li>
          </ul>
        </aside>
      </div>
    );
  }
}


export default Links;

src/components/links.js

我们 test/specs 目录最好也还是和 src 目录保持,只是测试的文件添加 .test.js 来表示它是用于测试的文件。

import React from 'react';  
import Links from '../../src/components/links';  
import { shallow } from '../../enzyme';


describe('Links component test', () => {

  it('render links', () => {
    const wrapper = shallow(
      <Links />
    );
    expect(wrapper.find('li').length).toBe(3);
  });

})

大概就是确定我们渲染后的节点,内容,关于 wrapper 支持的 API 。可以参考 Rendering API

模拟交互事件

当然,如果我们组建比较复杂不单单只是一些属性的判断而还有一些事件的交互,比如点击,输入框内容变化,等等。

我们可以在 src/components 下 建立一个 form.js

import React, { Component } from 'react';

class Form extends Component {  
  constructor(props) {
    super(props);
    this.state = {
      phone: 123,
      error: false,
    };
    this.onChange = this.onChange.bind(this);
    this.reset = this.reset.bind(this);
  }

  onChange(e) {
    const val = e.target.value;
    if (!/^[1][3,4,5,7,8][0-9]{9}$/.test(val)) {
      this.setState({
        error: true,
      });
    } else {
      this.setState({
        phone: val,
        error: false,
      });
    }
  }

  reset() {
    this.setState({
      phone: '',
      error: false,
    });
  }

  render() {
    const { phone, error } = this.state;
    return (
      <div className="m-form">
        <h3>This is form</h3>
        <p>
          <span className="js-contents">{phone}</span>
        </p>
        <p>
          <input type="text" onChange={this.onChange} />
        </p>
        {
          error && (<div className="js-error">Error Phone Number</div>)
        }
        <p><button onClikc={this.reset} type="button">Reset</button></p>
      </div>
    );
  }
}

export default Form;

一个简单的输入手机号并验证的情况。

enzyme 可以通过 simulate 来模拟 DOM 的交互事件,比如 click , focus , blur 等事件。

我们模拟用户输入手机可以这么写了

import React from 'react';  
import Form from '../../../src/components/form';  
import { mount } from '../../enzyme';


describe('Form component test', () => {

  it('render links', () => {
    const wrapper = mount(
      <Form />
    );
    expect(wrapper.find('input').length).toBe(1);
    expect(wrapper.find('button').length).toBe(1);
  });
})

我们通过 wrapper 先找到对应的输入框,这样我们可以来模拟输入值得变化。

判断函数调用

接触过单元测试的话,都知道 spy 。我们同理在 React 的测试用到 spy 来确定按钮点击等触发的函数调用。

it('# reset prop', () => {  
    const foo = {
      count: 0,
      setBar() {
        this.count ++;
      }
    };

    spyOn(foo, 'setBar');
    const wrapper = mount(
      <Form
        reset={foo.setBar}
      />
    );
    wrapper.find('button').simulate('click');
    expect(foo.setBar).toHaveBeenCalled();
  });

如果你希望判断调用次数以及传入的参数可以查看 section-Spies 的文档。

Redux 引入

现在越来越多的 React 应用引入了 redux

比如我们设计一个简单的引入拉取数据的的程序,然后渲染列表。

具体 Demo Code

/* eslint-disable arrow-parens */
import React, { Component } from 'react';  
import { connect } from 'react-redux';  
import { fetchList, fetchTags } from '../actions/guide';


class Guide extends Component {

  componentDidMount() {
    this.fetchList();
  }

  fetchList() {
    const { dispatch } = this.props;
    dispatch(fetchList());
    dispatch(fetchTags());
  }

  render() {
    const { data, tags} = this.props;
    let movies = null;
    let tagCom = null;
    if (tags && tags.length > 0 ) {
      tagCom = tags.map((item) => {
        return (
          <a href="#">{ item }</a>
        );
      });
    }
    if (data && data.length > 0 ) {
      movies = data.map((item) => {
        return (
          <div>
            <h3>{item.name}</h3>
            <p> :sparkles: {item.rank}</p>
          </div>
        );
      });
    }
    return (
      <div>
        <h3>This is guide</h3>
        <h4>Redux Test</h4>
        <p className="tag-list">{ tagCom }</p>
        { movies }
      </div>
    );
  }
}

const mapStateToProps = (state) => ({  
  data: state['guide']['data'],
  tags: state['guide']['tags'],
});

export default connect(mapStateToProps)(Guide);

如果我们需要测试引入 store 的组件,我们需要先安装 redux-mock-store

npm install redux-mock-store --save-dev

接下来,我们先写模拟数据状态的文件,新建 test/fixtures/state.js

const mockState = {  
  guide: {
    data: [
      {
        name: 'KungFu Hustle',
        rank: '8.3',
      },
      {
        name: 'CJ-7',
        rank: '6.4',
      },
    ],
    tags: ['movie', 'comedy'],
  },
};

export default mockState;

编写我们的测试文件 guide.test.js

import React from 'react';  
import configureStore from 'redux-mock-store'  
import { Provider } from 'react-redux';  
import Guide from '../../../src/components/guide';  
import { mount } from '../../enzyme';  
import mockState from '../fixtures/state';


const mockStore = configureStore();  
let wrapper;  
let store;  
beforeEach(() => {  
  // 创建关联 store
  store = mockStore(mockState);
  // 渲染测试的组件将 store 传入
  wrapper = mount(<Provider store={store}><Guide /></Provider>)
});
describe('Links component test', () => {  
  it('render movies and tags', () => {
    expect(wrapper.find('.movie-item').length).toBe(2);
    expect(wrapper.find('.tag-item').length).toBe(2);
  });
});

这样就可以针对引入 redux 的组件进行测试。

最后你可以在 yarn-react-webpack-seed 找到相关演示代码。

小结

本文在原有测试基础上,增加了对组件的事件交互和数据交互的测试,对于前端而言无论是 TDD 还是 BDD, 尝试去写自己组件的测试用例都有助于工程质量的提升和本身产品的效能开发,不过写好测试依旧需要花些时间。

扩展阅读


以上所述就是小编给大家介绍的《使用 Enzyme 进行 React 组件测试进阶》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

计算智能导论

计算智能导论

英吉布雷切特 / 谭营 / 清华大学出版社 / 2010-6 / 59.00元

《计算智能导论(第2版)》导论性地介绍了计算智能的5 个典型范例:人工神经网络、进化计算、计算群体智能、人工免疫系统和模糊系统。它们分别是对生物神经系统、生物进化过程、社会组织的群体行为、自然免疫系统和人类思维过程的成功建模。这些范例已经得到了广泛深入的研究,人们在取得了很大的成功之后,已将研究成果广泛地应用到了众多的实际应用领域。极大提高了人们发现问题,求解问题,尤其是求解复杂科学与工程问题的能......一起来看看 《计算智能导论》 这本书的介绍吧!

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

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试