Toggle navigation
痞子棠
主页
About Me
归档
标签
痞子棠
一个不会钓鱼的程序员不是一个好设计师
Jest笔记
无
2016-10-28 18:03:14
654
0
0
wangying6412
#声明 > *2017-03-04* > *本笔记针对 jest@v19.0.2 , 不保证内容的长期有效性。* > 这是笔记,不是教程。 -------------------------------- #准备/安装 ``` npm install jest -g //如果用了es6,react,之类的,装依赖吧。 //这是对babel的依赖包 npm install --save-dev babel-jest //react的 npm install --save-dev react-test-renderer ``` ``` //或者 npm install create-react-app -g create-react-app your_app cd your_app npm test //so happy ^_^ ``` # 配置 ``` //package.json ... "jest": { //"preset": "react-native", //react-native "testPathIgnorePatterns": [ //要忽略的目录 "<rootDir>/template/", "<rootDir>/node_modules/", "<rootDir>/ios/", "<rootDir>/android/", "<rootDir>/misc/" ] } ... ``` ## eslint 的配置 ``` "env": { ... "jest": true }, ``` # 生成覆盖率 ``` jest --coverage ``` #开始 ``` //sum.js module.exports = (a,b)=>(a + b); ``` ``` //新建 __tests__ 文件夹 //在 __tests__ 中新建测试文件sum-test.js test('测试1+2=3',()=>{ //实例化测试对象 const sum = require('../sum'); //设置期望值,进行测试(断言) expect(sum(1,2)).toBe(3); }); ``` ``` //修改你的package.json (可以用npm init命令创建) "scripts": { "test": "jest" } ``` 运行 `npm test` 命令,查看测试结果 ---------------------------------- #API ##全局对象 > 这些函数是放入全局环境中的,不用单独引入 ###beforeAll(fn) ###beforeEach(fn) ###afterAll(fn) ###afterEach(fn) ###describe(name, fn) ###describe.only(name, fn) ###describe.skip(name, fn) ###require.requireActual(moduleName) ###require.requireMock(moduleName) ###test(name, fn) ###test.only(name, fn) ###test.skip(name, fn) -------------------------- ##断言 expect() ``` expect(value) .toBe(value) //是 .not //非 .toBeCalled() //被调用 .toHaveBeenCalled() //已被调用 .toHaveBeenCalledTimes(number) //已被调用次数 //已经被调用 .toBeCalledWith(arg1, arg2, ...) .toHaveBeenCalledWith(arg1, arg2, ...) //已经最后被调用With .lastCalledWith(arg1, arg2, ...) .toHaveBeenLastCalledWith(arg1, arg2, ...) .toBeCloseTo(number, numDigits) //接近,约等于 .toBeDefined() //已定义 .toBeUndefined() //未定义 .toBeGreaterThan(number) //大于 .toBeGreaterThanOrEqual(number) //大于等于 .toBeLessThan(number) //小于 .toBeLessThanOrEqual(number) //小于等于 .toBeInstanceOf(Class) //是这个对象的实例 .toBeNull() //null .toBeTruthy() //true .toBeFalsy() //false .toContain(item) //被包含 .toContainEqual(item) //等于item .toEqual(value) //等于 .toMatch(regexp) //匹配正则 .toMatchSnapshot() //匹配快照 //抛出错误 .toThrow() .toThrowError(error) .toThrowErrorMatchingSnapshot() ``` ### .toThrowError() `toThrowError`接受的参数为函数,使用时如果测试对象需要先执行,可以将其包一层函数。例如: ``` expect(()=>dispatch(action('xxx','xxx'))).toThrowError('xxxx'); ``` --------------------------------------- #对react进行测试 ## 快照测试 > 所谓快照测试,就是对一个组件的UI按照各个事件的触发顺序,生成一组代码,做为正确的样本,保存在为`.sanp`文件。下次运行测试时,将会与该组代码进行比对(而不是再次生成快照),如果无法匹配,测试将会失败。按U可再次生成快照,并将当前代码保存为正确的样本。 ``` //安装 测试渲染器 //是的,renderer后面有两个'er',并没有写错 npm install react-test-renderer --save-dev ``` ``` //假设我们有一个Link.react.js模块,当光标移入时,它就会改变className,移出时又还原className。 //Link.react.js 代码可以去 官方API查看 //这是我们的测试代码 import React from 'react'; import Link from '../Link.react'; //引入测试渲染器 import renderer from 'react-test-renderer'; test('Link组件的hover事件触发', () => { //创建一个虚拟的component const component = renderer.create( <Link page="http://www.facebook.com">Facebook</Link> ); //得到一组快照(如果本地没有快照,就保存到本地) let tree = component.toJSON(); //验证是否与本地的快照匹配(如果本地保存有快照) expect(tree).toMatchSnapshot(); //手动触发组件的onMouseEnter事件 tree.props.onMouseEnter(); tree = component.toJSON(); //渲染 expect(tree).toMatchSnapshot(); //比对 //触发onMouseLeave事件 tree.props.onMouseLeave(); tree = component.toJSON(); expect(tree).toMatchSnapshot(); }); ``` ###如果使用了 redux-react **你需要传递一个mock的store到组件中** ``` import React from 'react'; import 'react-native'; //这是native的内容,react请忽略 import renderer from 'react-test-renderer'; //如果你使用了immutable import Immutable from 'immutable'; let store = { dispatch : ()=>{}, //如果你没有用immutable,这里返回普通对象就好 getState : ()=>Immutable.fromJS({simpleForm:{}}), subscribe : ()=>{}, }; //这是一个mock的其它组件,在你的测试对象中有引入它,对react进行测试,你需要经常模拟组件 jest.mock('../../Dui',()=>({Body:'Body'})); //这是你要测试的组件 import SimpleForm from '../SimpleForm.js'; test('快照测试',()=>{ const tree = renderer.create( //注意这里,将store传进去 <SimpleForm store={store} /> ).toJSON(); expect(tree).toMatchSnapshot(); }); ``` ###react-native Store 的更新及UI刷新 ``` //使用redux-react时,由于是浅渲染,Enzyme并不会在dispatch后触发UI的刷新(即mapStateToProps)。 //我们需要在模拟点击或者其它更新store的操作后主动重新渲染UI。 //示例代码: //根对象,包含<UI />,但是并不会渲染<UI /> const wrapper = ()=>shallow(<Reasons store={Store} compalete={callback} />); //UI,我们需要主动再将UI渲染一次 let UI = ()=>wrapper().find('UI').shallow(); //调用时执行wrapper() 或者 UI() let listRow = UI().find('ListRow').at(1); expect(listRow.props().children.props.children).toBe('adsfasdf'); //触发点击了,假如此时也触发了dispatch listRow.simulate('press'); //需要再次调用UI(),重新渲染UI,否则mapStateToProps是不会触发的 let btn = UI().find('TouchableOpacity').at(0); btn.simulate('press'); expect(pressItem).toEqual('asdfasdf'); ``` ###对中间件的模拟 > 使用插件redux-mock-store ``` npm install redux-mock-store --save-dev //具体使用方法自己去github上找 ``` ##DOM 测试 > DOM测试比较好理解,它依赖于Enzyme或者Test Utils. > (React Test Utils: React 官方的测试工具。Enzyme是对其的封装,使其更易于使用。) >Enzyme是一个用于React的JS测试程序; >它可以操作虚拟的DOM,取值,然后做各种断言。 >它的API跟jQuery非常像。 > API笔记见 - Enzyme笔记 **不要与快照同时使用,会有冲突。** ``` //安装 npm install enzyme --save-dev npm install react-addons-test-utils --save-dev npm install react-dom --save-dev ``` ``` /* *假设我们有一个Checkbox模 *其结构如下: * <label> * <input type="checkbox"> * <span>text</span> * </label> *它接受两个props,lableOn="已选择" labelOff="未选择" *分别用来显示checkbox不同状态时的文字 *具体代码参见官方文档 */ //我们的测试代码如下 import React from 'react'; //这是我们要测试的模块 import CheckboxWithLabel from '../CheckboxWithLabel'; //引入enzyme的shallow模块,即浅渲染 import {shallow} from 'enzyme'; it('当checkbox被点击时,它的text()值会改变', () => { //渲染一个dom出来 const checkbox = shallow( <CheckboxWithLabel labelOn="已选择" labelOff="未选择" /> ); //断言它的初始值(即未选择时)为props传递的值 expect(checkbox.text()).toEqual('未选择'); //触发input:checkbox的change事件 checkbox.find('input').simulate('change'); //取.test()值,并断言其为props.labelOn expect(checkbox.text()).toEqual('已选择'); }); ``` ---------------------------- ---------------------------- #React-Native的测试 ##配置package.json ``` "scripts": { "test": "jest --watch" }, "jest": { "preset": "react-native" } ``` ##如果使用了babel ``` npm install babel-jest --save-dev ``` ##快照测试 > 与React-Dom的测试并无二致 ``` //安装模块 npm install react-test-renderer --save-dev ``` *注意,引入模块时要先引入React-native* ``` import 'react-native'; //在react-native之后引入: import renderer from 'react-test-renderer'; ``` ##用`jest.mock`来模拟Native模块 > jest-react-native本地预设提供了一些应用于“react-native”仓库的默认mock。 但是,一些“react-native”组件或第三方组件依赖于原生代码。 在这种情况下,Jest的手动模拟系统可以帮助模拟。 ``` /* 例如,如果您的代码依赖于第三方原生视频组件react-native-video,您可能需要使用手动模拟,如下所示: */ jest.mock('react-native-video', () => 'Video'); /* 它将组件渲染为<Video {... props} />并其所有的props在快照输出。 */ /* 有时你想模拟更复杂的组件。 假如,如果你想将一个原生组件的props或静态字段转发到一个mock,你可以通过以下代码从jest-react-native返回一个不同的React组件: */ //我完全没搞懂这什么意思 ^_^,mock 套 mock吗?? jest.mock('path/to/MyNativeComponent', () => { const mockComponent = require('react-native/jest/mockComponent'); return mockComponent('path/to/MyNativeComponent'); }); ``` ##配置 ###预处理或忽略的模块 ``` "preprocessorIgnorePatterns": [ "node_modules/(?!react-native|my-project|react-native-button)" ] ``` ###指定模块的路径 > 默认情况下,预设所有模块都在node_modules下开始找,但如果找不到模块,则需要配置。 ``` "moduleNameMapper": { "my-module.js": "<rootDir>/path/to/my-module.js" } ``` ------------------------------- #异步数据模拟 ##模拟异步模块 ``` // 假如我们有一个模块,从服务器拉取一份用户资料,并返回一个name值。 //user.js import request from './request'; export function getUserName(userID) { return request('/users/' + userID).then(user => user.name); } ``` ``` //实际上,我们测试时并不想访问网络,那么我们可以模拟这次请求。 //现在,我们与__test__并列建立一个__mocks__文件夹,用来放模拟网络请求的文件。 // __mocks__/request.js const users = { 4: {name: 'Mark'}, 5: {name: 'Paul'}, }; export default function request(url) { return new Promise((resolve, reject) => { const userID = parseInt(url.substr('/users/'.length), 10); process.nextTick( () => users[userID] ? resolve(users[userID]) : reject({ error: '没有找到ID为' + userID + '的用户.', }) ); }); } ``` ``` //user.test.js //让jest模拟request模块,它会自动从__mocks__里加载 //然后当user模块调用request时,会返回我们模拟的数据 jest.mock('../request'); import user from '../user'; it('user获取异步数据', () => { return user.getUserName(5) .then(name => expect(name).toEqual('Paul')); }); ``` ##也可以使用nock插件,拦截请求 ``` npm install nock --save-dev ``` ``` import nock from 'nock'; let res_nock = nock('http://www.test.com') .get('/user/1') .reply(200, { _id: '123ABC', _rev: '946B7D1C', username: 'pgte', email: 'pedro.teixeira@gmail.com' }); ``` --------------------------------- ``` 分割线 ```
上一篇:
Enzyme笔记
下一篇:
sass 学习笔记
0
赞
654 人读过
新浪微博
微信
更多分享
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册