React组件单元测试

React组件自动化测试

1. 为何要进行自动化测试

当咱们编写一个组件的时候,要怎么保证组件功能能达到预期呢?你可能回答:我能够人工测试。可是当经历三四次迭代,当有多人协调开发,当进行重构的时候,如何能快速的验证组件是否依然正确执行?这正是须要自动化测试的缘由。不管经历多少次迭代,好的自动化测试都能保证你的组件可以正确执行。javascript

A component that is untestable or hard to test is most likely badly designed.前端

通过测试的组件是可靠的,可测的组件的架构是合理的。若是一个组件难如下手编写测试用例,只能证实这个组件的设计是糟糕的。所以,编写测试用例的同时,能够帮助组件开发者发现问题,调整代码使架构更加合理。java

2. 自动化测试的基础内容

(1) 自动化测试分类react

按照测试方法分类:黑盒测试和白盒测试segmentfault

黑盒测试也称功能测试,在测试中,把程序看做一个不能打开的黑盒子,在彻底不考虑程序内部结构和内部特性的状况下,在程序接口进行测试。用户对内部逻辑并不可见。react-native

白盒测试又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。白盒测试是一种测试用例设计方法,盒子指的是被测试的软件,白盒指的是盒子是可视的,你清楚盒子内部的东西以及里面是如何运做的。”白盒"法全面了解程序内部逻辑结构、对全部逻辑路径进行测试。bash

(2) 测试金字塔微信

该概念是Mike Cohn 在他的著做《Succeeding with Agile》一书中提出的。架构

测试金字塔中提到的两件事:app

  • 编写不一样粒度的测试
  • 层次越高,你写的测试应该越少

(3) 单元测试

指对软件中的最小可测试单元进行检查和验证。单元测试做为测试金字塔最底层,粒度最小,测试速度最快,属于白盒测试。

大多数单元测试包括四个主体:测试套件describe、测试用例it、断定条件expect、断言结果toEqual。

(4) 测试覆盖率

传统的测试覆盖方法常见的有如下几种:

  • 函数覆盖(Function Coverage)
  • 语句覆盖(Statement Coverage)
  • 决策覆盖(Decision Coverage)
  • 条件覆盖(Condition Coverage
3. 前端自动化单元测试工具

Jest

Jest是一个轻量级的JavaScript测试框架,能够应用于Babel, TypeScript, Node, React, Angular, Vue等多种技术栈。

const sum = require('./sum');

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});
复制代码

Enzyme

翻译为“溶解酶”,做为单元测试渗透于代码各个细节。通常使用 Enzyme 中的 mount 或 shallow 方法,将目标组件转化为一个 ReactWrapper对象,并在测试中调用其各类方法:

import React from 'react';
import { expect } from 'chai';
import { render } from 'enzyme';

import Foo from './Foo';

describe('<Foo />', () => {
  it('renders three `.foo-bar`s', () => {
    const wrapper = render(<Foo />);
    expect(wrapper.find('.foo-bar')).to.have.lengthOf(3);
  });

  it('renders the title', () => {
    const wrapper = render(<Foo title="unique" />);
    expect(wrapper.text()).to.contain('unique');
  });
});
复制代码
4. 编写有价值的React组件单元测试用例

React组件分为四种:

  • 展现型业务组件
  • 容器型业务组件
  • 通用 UI 组件
  • 功能型组件

根据业务场景,本文主要针对功能性组件进行阐述。

功能型组件,指的是跟业务无关的另外一类组件:它是功能型的,更像是底层支撑着业务组件运做的基础组件,好比文本框组件、按钮组件等。功能性组件,更注重逻辑性,UI比较没有那么偏重。功能性组件通常包括JS跟CSS两部份内容,CSS部分不做为测试的重点。

功能性组件必须测试的三部分:

  • Props传入;
  • 组件分支渲染逻辑;
  • 事件调用和参数传递。

以上三部分测事后,将会达到较高的测试覆盖率。

本文以Karma+Webpack+Mocha+Chai+Sion+istanbul-instrumenter-loader解决方案做为示例演示如何覆盖以上功能性组件测试的三部分。

首先创建一个简单的Input组件,包含label跟input两个标签,能够定义label跟input的值。

import React from 'react';
import PropTypes from 'prop-types';

export default class TextInput extends React.Component {
  static propTypes = {
    label: PropTypes.string,
    defaultValue: PropTypes.string,
    onChange: PropTypes.func
  }

  static defaultProps = {
    label: '',
    defaultValue: ''
  }

  onChange(e) {
    var val = e.target.value;

    if (this.props.onChange) {
      this.props.onChange(val);
    }
  }


  render() {
    const {label, defaultValue, onChange} = this.props;
    return (
      <div> { label ? (<label>{label}</label>) : null } <input defaultValue={this.props.defaultValue} onChange={this.onChange.bind(this)} ></input> </div>
    )
  }
}

复制代码

首先,测试Props是否正确传入。

it('Validate attributes of the TextInput', () => {
  const props = {
    label: '测试',
    defaultValue: '测试值'
  }
  const wrapper = mount(<TextInput {...props}/>); expect(wrapper.find('label').text()).to.equal('测试'); expect(wrapper.find('input').prop('defaultValue')).to.equal('测试值'); }); 复制代码

其次,测组件分支渲染。

it('Can not render label When label is null', () => {
    const wrapper = mount(<TextInput />);
    expect(wrapper.find('label').length).to.be.empty;
  });
  it('Render label When label is not null', () => {
    const wrapper = mount(<TextInput label='up'/>);
    expect(wrapper.find('label').length).not.to.be.empty;
  });
复制代码

最后,测事件调用。

it('Validate onChange event of the TextInput', () => {
    var temp = '';
    const props = {
      onChange: function(value) {
        temp = value;
      }
    }
    const wrapper = mount(<TextInput {...props} value='测试值'/>); var input = wrapper.find('input'); input.simulate('change', { target: { value: 'Changed' } }); expect(temp).to.equal('Changed'); }); 复制代码

最后,跑一遍全部的测试用例,均是经过的。而后看下测试覆盖率,分支覆盖率为75%。

进一步打开详情查看,原来是有一部分代码仅有if而没有else,因此这部分未达到100%是能够忽略的。

若是十分介意,能够在if前加入/* istanbul ignore else */。

onChange(e) {
    var val = e.target.value;
    /* istanbul ignore else */
    if (this.props.onChange) {
      this.props.onChange(val);
    }
  }
复制代码

这样就能够达到100%了。

总结

本文上半部分主要阐述了自动化测试的一些基础知识,下半部分经过一个textinput组件实例,阐述了如何编写有价值的测试用例,达到对组件代码的全覆盖。

参考

React 单元测试策略及落地

测试金字塔实战

前端自动化测试概览

对 React 组件进行单元测试

Web 前端单元测试到底要怎么写

7 architectural attributes of a reliable React component

测试覆盖(率)到底有什么用?

做者简介

南溪,铜板街前端开发工程师,2016年4月加入团队,目前主要负责资金端交易侧 H5项目开发。

本文主要经过具体的案例讲述了React组件单元测试的过程,如需获取更多React相关内容,请扫码关注 “铜板街科技” 微信公众号,并在后台回复“React” 或者 “react-native” 获取更多精彩内容。
相关文章
相关标签/搜索