jest+enzyme为react项目加入单测——2、基础实例

很多教程的实例对新手并不友好,这里的例子,都是笔者自己写的,希望适合大家的胃口。首先,我们要构建一个react项目,具体方法请参考《从零搭建前端开发环境》系列。当然,如果自己已经有了一套环境,那么下面将会展示demo的业务代码,可根据自己的情况进行调整。

0、安装配置jest + enzyme

详见用jest构建react项目的测试框架——1、安装与配置,与前文不一样的是,这里会采用react@16,而且会更改一些jest.config.js里面的配置,尤其是testUrl这一项,请同学们留心。

1、业务代码

src/index.jsx,项目入口文件,在jest.config.js里面把它ignore了吧,实在没必要做什么单测。

import React from 'react';
import ReactDOM from 'react-dom';
import HelloWorld from 'components/HelloWorld';
import './index.less';

ReactDOM.render(
  <HelloWorld />,
  document.getElementById('app'),
);

src/index.less

#app {
  // It is suggested to set min-width of the wrapper
  min-width: 900px;
}

src/components/HelloWorld/index.jsx,组件功能很简单,就是点击按钮,出现“Hello World”,当然还加入了样式、图片和方法引用,尽量保证测试的全面性。里面还有一道思考题,当作一个彩蛋吧。

import React from 'react';
import { formatDate } from 'util/index';
import logo from 'assets/logo.jpg';
import './index.less';

export default class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      txt: '',
    };
  }

  onShowTxt() {
    this.setState({
      txt: 'Hello World',
    });
  }

  render() {
    return (
      <div className="hlwd">
        <img src={logo} alt="logo" />
        <p className="hlwd-note">When you click the btn, the time will change!</p>
        <p>Because function render will be called.Think it deeply to understand it!</p>
        <h1>{formatDate(new Date())}</h1>
        <button onClick={() => this.onShowTxt()}>Show Hello World</button>
        <p>{this.state.txt}</p>
      </div>);
  }
}

src/components/HelloWorld/index.less(推荐样式采用.a-b-c的这种形式,不会改变权重,缺点就是html里写的会有点长)

.hlwd {
  text-align: center;
  width: 500px;
  margin: 0 auto;
  &-note {
    font-weight: bold;
  }
}

src/util/index.js,常用的工具函数,觉得有用可以拿走哦。

/**
 * 获取url的query
 */
export function getQuery(param) {
  const reg = new RegExp(`(^|&)${param}=([^&]*)(&|$)`);
  const r = window.location.search.substr(1).match(reg);
  return r != null ? decodeURIComponent(r[2]) : null;
}

/**
 * 获取cookie
 */
export function getCookie(name) {
  const reg = new RegExp(`(^| )${name}=([^;]*)(;|$)`);
  const match = document.cookie.match(reg);
  return match ? match[2] : null;
}

/**
 * 将 Date 转化为指定格式的String
 * 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
 * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
 * 例子:
 * formatDate(new Date(), "yyyy-MM-dd hh:mm:ss.S") => 2006-07-02 08:09:04.423
 * formatDate(new Date(), "yyyy-M-d h:m:s.S") => 2006-7-2 8:9:4.18
 */
export function formatDate(date, format = 'yyyy-MM-dd hh:mm:ss') {
  if (Object.prototype.toString.call(date) !== '[object Date]') {
    return null;
  }
  let fmt = format;
  const o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds(), // 毫秒
  };
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(RegExp.$1, (`${date.getFullYear()}`).substr(4 - RegExp.$1.length));
  }
  let tmp;
  Object.keys(o).forEach((k) => {
    if (new RegExp(`(${k})`).test(fmt)) {
      tmp = o[k];
      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? tmp : (`00${tmp}`).substr((`${tmp}`).length));
    }
  });
  return fmt;
}

2、js单测

像工具函数这类的纯js的单测,是比较好写的,也不用太配置,只用jest就够了。下面我们对util/index.js进行单测,主要是让大家熟悉一下语法,详见jest文档

test/spec/util.spec.js

import { getQuery, getCookie, formatDate } from 'util/index';

describe('# getQuery', () => {
  /** 
   * 这里就涉及到了jest.config.js里面配置的testURL,这里是不能动态修改location.href的,不信可以试试
   * testURL: 'https://test.com?empty=&num=0&str=str&cstr=中文&encode=%e4%b8%ad%e6%96%87',
   */
  it('empty => ""', () => {
    expect(getQuery('empty')).toBe('');
  });

  it('num => 0', () => {
    expect(getQuery('num')).toBe('0');
  });

  it('str => str', () => {
    expect(getQuery('str')).toBe('str');
  });

  it('cstr => 中文', () => {
    expect(getQuery('cstr')).toBe('中文');
  });

  it('encode => 中文', () => {
    expect(getQuery('encode')).toBe('中文');
  });
  
  it('null => null', () => {
    expect(getQuery('null')).toBeNull();
  });
});

describe('# getCookie', () => {
  /**
   * 这里可以操作cookie
   */
  document.cookie = 'key1=value1;';
  document.cookie = 'key2=value2';

  it('getCookie("key1") => "value1"', () => {
    expect(getCookie('key1')).toBe('value1');
  });

  it('getCookie("key2") => "value2"', () => {
    expect(getCookie('key2')).toBe('value2');
  });

  it('getCookie("null") => null', () => {
    expect(getCookie('null')).toBeNull();
  });
});

describe('# formatDate', () => {
  const DATE_0 = new Date(0);

  it('formatDate(DATE_0) => "1970-01-01 08:00:00"', () => {
    expect(formatDate(DATE_0)).toBe('1970-01-01 08:00:00');
  });

  it('formatDate(DATE_0, "M-d h:m:s") => "1-1 8:0:0"', () => {
    expect(formatDate(DATE_0, 'M-d h:m:s')).toBe('1-1 8:0:0');
  });

  it('formatDate("test") => null', () => {
    expect(formatDate('test')).toBe(null);
  });
});

运行

$ npm test

src/util/index.js是不是已经100%覆盖了?浏览器打开test/coverage/lcov-report/index.html,可以看到详情,很人性化。当然,我们还没有写react组件的测试,覆盖率可以暂时忽略。

3、React组件单测

创建src/components/HelloWorld/__tests__/index.spec.js,比较推荐把组件的单测就近放置,留心我的目录结构哦。

import React from 'react';
import { shallow } from 'enzyme';
import HelloWorld from '..';

describe('<HelloWorld />', () => {
  const wrapper = shallow(<HelloWorld />);
  it('Renders an img', () => {
    expect(wrapper.find('img').length).toBe(1);
  });

  it('Before click the btn', ()=>{
    expect(wrapper.find('p').at(2).text()).toBe('');
  });

  it('After click the btn', ()=>{
    wrapper.find('button').simulate('click');
    expect(wrapper.find('p').at(2).text()).toBe('Hello World');
  });
});
运行

$ npm test

怎么样,都100%通过了吧,强迫症的同学们可以松一口气了。

注:其实这一步很容易出问题,正如用jest构建react项目的测试框架——1、安装与配置提到的,less、img、react版本等,都有可能报错,出了问题很正常,再领会一下前一篇文章的精神吧。

4、项目结构参考

不要被这个目录结构搞得一脸懵,请移步《从零搭建前端开发环境》系列,你也可以一步一步搭建起自己的前端工程。

demo
  |- config/
    |- jest.config.js
    |- webpack.base.js
    |- webpack.dev.js
    |- webpack.prod.js
  |- src/
    |- assets/
      |- logo.jpg
    |- components/
      |- HelloWorld/
        |- __tests__/
          |- index.spec.js
        |- index.jsx
        |- index.less
    |- util/
      |- index.js
    |- index.jsx
    |- index.less
  |- test/
    |- spec/
      |- util.spec.js
    |- .eslintrc.js
    |- cssTransform.js
    |- fileTransform.js
    |- setup.js
  |- .babelrc
  |- .eslintignore
  |- .eslintrc.js
  |- .gitignore
  |- .postcssrc.js
  |- index.html
  |- package.json

猜你喜欢

转载自blog.csdn.net/zhaolandelong/article/details/79830752