React组件单元测试

发表于:2019-4-11 11:53

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:铜板街技术    来源:掘金

   React组件自动化测试
  1. 为什么要进行自动化测试
  当我们编写一个组件的时候,要怎么保证组件功能能达到预期呢?你可能回答:我可以人工测试。但是当经历三四次迭代,当有多人协调开发,当进行重构的时候,如何能快速的验证组件是否依然正确执行?这正是需要自动化测试的原因。无论经历多少次迭代,好的自动化测试都能保证你的组件能够正确执行。
  A component that is untestable or hard to test is most likely badly designed.
  经过测试的组件是可靠的,可测的组件的架构是合理的。如果一个组件难以下手编写测试用例,只能证明这个组件的设计是糟糕的。因此,编写测试用例的同时,可以帮助组件开发者发现问题,调整代码使架构更加合理。
  2. 自动化测试的基础内容
  (1) 自动化测试分类
  按照测试方法分类:黑盒测试白盒测试
  黑盒测试也称功能测试,在测试中,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,在程序接口进行测试。用户对内部逻辑并不可见。
  白盒测试又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。白盒测试是一种测试用例设计方法,盒子指的是被测试的软件,白盒指的是盒子是可视的,你清楚盒子内部的东西以及里面是如何运作的。”白盒"法全面了解程序内部逻辑结构、对所有逻辑路径进行测试。
  (2) 测试金字塔
  该概念是Mike Cohn 在他的著作《Succeeding with Agile》一书中提出的。
  测试金字塔中提到的两件事:
  编写不同粒度的测试
  层次越高,你写的测试应该越少
  (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组件实例,阐述了如何编写有价值的测试用例,达到对组件代码的全覆盖。

     上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号