react native添加单元测试中的库
测试代码的当前版本是
"react": "16.0.0",
"react-native": "^0.48.4",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-logger": "^2.7.4",
"redux-thunk": "2.2.0",
因为现在的react native很多都会使用redux,所以测试中有针对这部分的内容。
需要用到的库
"babel-jest": "19.0.0",
"babel-polyfill": "^6.26.0",
"babel-preset-react-native": "1.9.1",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
"chai-enzyme": "^1.0.0-beta.0",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"jest": "19.0.2",
"react-dom": "^16.2.0",
"react-test-renderer": "15.4.2",
"redux-mock-store": "^1.4.0",
"sinon": "^4.1.6",
"sinon-chai": "^2.14.0"
chai-enzyme 在默认安装的时候版本号是0.8.0,这个版本不能兼容到”enzyme”: “^3.3.0”,需要使用最新的1.0.0-beta.0版本
jest的配置
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.jsx?$": "babel-jest"
}
}
jest加入 transform的 babel-jest配置是为了避免出现jest不能识别es6的import等关键字。
待测组件代码
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Actions } from 'react-native-router-flux';
import * as profileActions from '../modules/users/reducers/profileActions';
import React, { Component } from 'react';
import {
View,
Text,
TouchableOpacity,
} from 'react-native';
function mapStateToProps(state) {
return {
profile: state.profile
};
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({ ...profileActions }, dispatch)
};
}
export class TestView extends Component {
constructor(props) {
super(props);
}
render() {
try {
let userNameControl;
if (this.props.username) {
userNameControl = <Text>this.props.username</Text>
}
return (
<View style={{
backgroundColor: 'white', flexGrow: 1,
alignItems: 'center', justifyContent: 'center'
}}>
<TouchableOpacity style={styles.Button} onPress={() => {
this.props.actions.getProfile();
}}>
<Text style={{ color: 'white' }}>Get Profile</Text>
</TouchableOpacity>
{userNameControl}
</View>
);
} catch (error) {
return <View></View>
}
}
}
let styles = {
Button: {
alignItems: 'center', justifyContent: 'center',
backgroundColor: '#286090',
padding: 10,
borderRadius: 5
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TestView);
需要注意的代码是
export class TestView extends Component
上面这行是为了不去引入redux,而在接下来的测试代码中自己来引入props的数据。
export default connect(mapStateToProps, mapDispatchToProps)(TestView);
如果用上面这行代码创建的class测试就需要去创建store,引用sense等组件,实在太过麻烦。
测试代码
import React from 'react'
import { bindActionCreators } from 'redux'
import { TestView } from '../TestView'
import {
View,
Text,
TouchableOpacity
} from 'react-native'
require('babel-polyfill')
import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import sinon from 'sinon'
import chai from 'chai'
import sinonChai from 'sinon-chai'
import chaiAsPromised from 'chai-as-promised'
import chaiEnzyme from 'chai-enzyme'
chai.use(sinonChai)
chai.use(chaiAsPromised)
chai.use(chaiEnzyme())
global.chai = chai
global.sinon = sinon
global.expect = chai.expect
global.should = chai.should()
configure({ adapter: new Adapter() });
import ReactTestUtils from 'react-dom/test-utils';
import { shallow } from 'enzyme'
describe('TestView', () => {
let _props, _spies, _wrapper
let eventCodeControlProps = {
global: {
language: {
EventCodeEmpty: 'test',
OK: 'OK',
EventCodePlaceHolder: 'EventCodePlaceHolder',
EventCode: 'EventCode',
PromoCode: 'PromoCode'
}
}
}
beforeEach(() => {
_spies = {}
_props = {
...eventCodeControlProps,
actions: bindActionCreators({
getProfile: (_spies.getProfile = sinon.spy())
}, _spies.dispatch = sinon.spy())
};
_wrapper = shallow(<TestView {..._props} />);
})
it('Should has one View.', () => {
expect(_wrapper.is('View')).to.equal(true);
})
it('Should has one children.', () => {
expect(_wrapper.children()).to.have.length(1);
})
it('Each element of form should be <TouchableOpacity>.', () => {
_wrapper.children().forEach(function (node) {
expect(node.is(TouchableOpacity)).to.equal(true);
})
})
it('Get and show username', () => {
_spies = {}
_props = {
...eventCodeControlProps,
actions: bindActionCreators({
getProfile: (_spies.getProfile = () => {
_wrapper.setProps({ username: '123' })
})
}, _spies.dispatch = sinon.spy())
};
_wrapper = shallow(<TestView {..._props} />);
let to = _wrapper.find('TouchableOpacity');
to.simulate('press');
expect(_wrapper.children()).to.have.length(2);
})
})
最后一个测试用例复杂一些,可以测试按钮的点击动作。
to.simulate(‘press’);
会触发TouchableOpacity控件的onPress事件。onPress事件调用的函数中调用了
this.props.actions.getProfile();
这个函数则在
_props = {
...eventCodeControlProps,
actions: bindActionCreators({
getProfile: (_spies.getProfile = () => {
_wrapper.setProps({ username: '123' })
})
}, _spies.dispatch = sinon.spy())
};
中做了mock。接下来的
_wrapper.setProps({ username: ‘123’ })
会触发控件的render函数。
this.props.username === ‘123’
userNameControl就会显示出来
expect(_wrapper.children()).to.have.length(2);
子组件就会变成两个。