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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章