當咱們編寫一個組件的時候,要怎麼保證組件功能能達到預期呢?你可能回答:我能夠人工測試。可是當經歷三四次迭代,當有多人協調開發,當進行重構的時候,如何能快速的驗證組件是否依然正確執行?這正是須要自動化測試的緣由。不管經歷多少次迭代,好的自動化測試都能保證你的組件可以正確執行。javascript
A component that is untestable or hard to test is most likely badly designed.前端
通過測試的組件是可靠的,可測的組件的架構是合理的。若是一個組件難如下手編寫測試用例,只能證實這個組件的設計是糟糕的。所以,編寫測試用例的同時,能夠幫助組件開發者發現問題,調整代碼使架構更加合理。java
(1) 自動化測試分類react
按照測試方法分類:黑盒測試和白盒測試segmentfault
黑盒測試也稱功能測試,在測試中,把程序看做一個不能打開的黑盒子,在徹底不考慮程序內部結構和內部特性的狀況下,在程序接口進行測試。用戶對內部邏輯並不可見。react-native
白盒測試又稱結構測試、透明盒測試、邏輯驅動測試或基於代碼的測試。白盒測試是一種測試用例設計方法,盒子指的是被測試的軟件,白盒指的是盒子是可視的,你清楚盒子內部的東西以及裏面是如何運做的。」白盒"法全面瞭解程序內部邏輯結構、對全部邏輯路徑進行測試。bash
(2) 測試金字塔微信
該概念是Mike Cohn 在他的著做《Succeeding with Agile》一書中提出的。架構
測試金字塔中提到的兩件事:app
(3) 單元測試
指對軟件中的最小可測試單元進行檢查和驗證。單元測試做爲測試金字塔最底層,粒度最小,測試速度最快,屬於白盒測試。
大多數單元測試包括四個主體:測試套件describe、測試用例it、斷定條件expect、斷言結果toEqual。
(4) 測試覆蓋率
傳統的測試覆蓋方法常見的有如下幾種:
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');
});
});
複製代碼
React組件分爲四種:
根據業務場景,本文主要針對功能性組件進行闡述。
功能型組件,指的是跟業務無關的另外一類組件:它是功能型的,更像是底層支撐着業務組件運做的基礎組件,好比文本框組件、按鈕組件等。功能性組件,更注重邏輯性,UI比較沒有那麼偏重。功能性組件通常包括JS跟CSS兩部份內容,CSS部分不做爲測試的重點。
功能性組件必須測試的三部分:
以上三部分測事後,將會達到較高的測試覆蓋率。
本文以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組件實例,闡述瞭如何編寫有價值的測試用例,達到對組件代碼的全覆蓋。
7 architectural attributes of a reliable React component
南溪,銅板街前端開發工程師,2016年4月加入團隊,目前主要負責資金端交易側 H5項目開發。