React Native的单元测试Jest+Enzyme+storybook
配置
Jest配置
1. 安装
Enzyme配置
1. 安装
yarn add enzyme enzyme-adapter-react-16 --dev
每个适配器可能还有其他的对等体依赖关系,您也需要安装它们。举例来说,
enzyme-adapter-react-16
对应用同版本的依赖react@16
,react-dom@16
和react-test-renderer@16
。
2. 初始化配置
yarn add react-native-mock --dev
3. 初始化配置
做后需要配置enzyme的适配器,一个一般要根据react的版本配置,现在项目中用的是react@16,所以如下配置
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'react-native-mock/mock';
Enzyme.configure({ adapter: new Adapter() });
还需要让此配置,在所以test之前执行,进行如下设置
// package.json
// ...
"jest": {
// ...
"setupFiles": [
"<rootDir>/__tests__/Setup"
]
}
// ...
Storybook配置
1. 安装
执行下面三条指令就能完成安装
cd my-project-directory
npm i -g @storybook/cli
getstorybook
2. 运行
npm run storybook
运行
Jest 运行
-
运行全部测试用例
npm jest
-
运行单个测试用例,可以借助webstorm工具,来运行,非常方便
image
Storybook运行
-
在开发组件的时候要把storybook运行起来,并写stories
npm run storybook
用例
Jest 常用api用法实例
中文 | 英文 |
---|---|
API集合
全局方法
匹配器
Enzyme 常用api用法实例
enzyme有3种渲染方式:render
、mount
、shallow
,先了解下区别。
render
、mount
、shallow
的区别
render采用的是第三方库Cheerio
的渲染,渲染结果是普通的html结构,对于snapshot使用render
比较合适。
shallow和mount对组件的渲染结果不是html的dom树,而是react树,如果你chrome装了react devtool插件,他的渲染结果就是react devtool tab下查看的组件结构,而render
函数的结果是element tab下查看的结果。
这些只是渲染结果上的差别,更大的差别是shallow和mount的结果是个被封装的ReactWrapper
,可以进行多种操作,譬如find()、parents()、children()
等选择器进行元素查找;state()、props()
进行数据查找,setState()、setprops()
操作数据;simulate()
模拟事件触发。
shallow只渲染当前组件,只能能对当前组件做断言;mount会渲染当前组件以及所有子组件,对所有子组件也可以做上述操作。一般交互测试都会关心到子组件,我使用的都是mount
。但是mount耗时更长,内存啥的也都占用的更多,如果没必要操作和断言子组件,可以使用shallow。
交互测试
主要利用simulate()
接口模拟事件,实际上simulate是通过触发事件绑定函数,来模拟事件的触发。触发事件后,去判断props上特定函数是否被调用,传参是否正确;组件状态是否发生预料之中的修改;某个dom节点是否存在是否符合期望。
组件测试
-
用storybook做组件测试,既可以存储组件快照,也可以快速查看组件样式
例如:
// import React from 'react'; import { storiesOf } from '@storybook/react-native'; import { action } from '@storybook/addon-actions'; import { linkTo } from '@storybook/addon-links'; import * as img from './img'; import ImageButton from './ImageButton'; storiesOf('<ImageButton />', module) .add('normal', () => <ImageButton title={'确认'} imageName={img.ICON_DENE} onPress={action('点击')} /> ) .add('cancel', () => <ImageButton title={'取消'} imageName={img.ICON_CANCEL} onPress={action('点击')} /> ) ;
-
效果图如下
image image image image -
根据组件的需要,进行一些函数的测试,如下
// imageButton.test.js import 'react-native' import React from 'react'; import { mount, shallow } from 'enzyme'; import ImageButton from '../../src/components/ImageButton'; test('<ImageButton/>', () => { let i = 0 const onPress = () => I++ const wrapper = shallow(<ImageButton title={'1'} imageName={null} onPress={onPress}/>); expect(wrapper.instance().props.title).toBe('1'); // uses the right handler expect(wrapper.prop('onPress')).toBe(onPress) expect(i).toBe(0); wrapper.simulate('press'); expect(i).toBe(1); });
API测试
-
API测试主要进行,返回状态码(200、500、502等)的验证,必要字段的返回,指定参数传入指定数据返回等验证,API测试可以在和后台定义接口的时候就写,等后台接口写好后直接跑一下测试用例就可验证。
describe('api', () => { // ... test('/api/config', () => { expect.assertions(1); // 异步断言数量 const param = Object.assign(defaultParam, {}); const path = '/api/config'; return api.post(path, param) .then(response => response.data) .then(response => { expect(response.errorCode).toBe(200); }) }); // .... });