-
Notifications
You must be signed in to change notification settings - Fork 137
Description
ts测试jest
常见做法
第一步安装包: yarn add -D typescript jest ts-jest @types/jest
创建jest.config.js:
module.exports = {
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
}
base.spec.ts:
import { sampleFunction } from "../src";
test('adds 1 + 2 to equal 3', () => {
expect(sampleFunction("hello")).toBe("hellohello");
});
关于sessionStorage和localStorage的测试
在jest的最新版本中, 其实已经通过jsdom模拟了 sessionStorage和localStorage 的实现
然后测试的时候, 需要做异步存储, 要不然是不能获取到 存入的值。
例如有这样一个方法:
const sessionStore = {
get(k) {
let ret = window.sessionStorage.getItem(k);
try {
ret = JSON.parse(ret);
} catch (e) { console.log(e); }
return ret;
},
set(k, val) {
let value = val;
try {
value = JSON.stringify(val);
} catch (e) { console.log(e); }
window.sessionStorage.setItem(k, value);
},
};
我们写测试用例的时候, 可以这样写:
describe('sessionStore', () => {
it('set 方法', async (done) => {
await expect(sessionStore.set('name', 'yanle'));
done();
});
it('get 方法', (done) => {
done();
expect(sessionStore.get('name')).toBe('yanle');
});
});
组件测试测试中忽略样式加载和文件加载
在配置jest.config.js中加上这两句
moduleNameMapper: {
'^.+\\.(css|less)$': '<rootDir>/test/cssTransform.js',
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/test/fileTransform.js',
},
然后对应文件分别为:
cssTransform.js:
module.exports = {};
fileTransform.js:
module.exports = 'test-file-stub';
测试中关于antd组件报undefined的问题
这个问题不是个例, antd issue 里面有很多这种情况的讨论。
解决办法:
1、在.babelrc 文件里面 删除import-plugin 配置
{
"passPerPreset": true,
"plugins": [
"relay",
"transform-runtime",
**["import", {"libraryName": "antd", "style": "css"}]**
],
"presets": [
"react",
"es2015",
"stage-0"
]
}
2、关闭webpack react生产模式核心压缩功能即不会报错
remove these:
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify("production")
}
});
3、获取组件不要用解构
ReferenceError: Layout is not defined
import { Menu } from 'antd';
const { Item } = Menu;
While:All fine...
import { Menu } from 'antd';
const Item = Menu.Item;
Could not find "store" in either the context or props of "Connect(XXX)"
import React from "react";
import { shallow } from "enzyme";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import TestPage from "../TestPage";
const mockStore = configureMockStore();
const store = mockStore({});
describe("Testpage Component", () => {
it("should render without throwing an error", () => {
expect(
shallow(
<Provider store={store}>
<TestPage />
</Provider>
).exists(<h1>Test page</h1>)
).toBe(true);
});
});
- Nested components testing with Enzyme inside of React & Redux
这个文章里面给出了一个非常好的解决方案
Enzyme's mount takes optional parameters. The two that are necessary for what you need are
options.context: (Object [optional]): Context to be passed into the component
options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper
You would mount SampleComponent with an options object like so:
const store = {
subscribe: () => {},
dispatch: () => {},
getState: () => ({ ... whatever state you need to pass in ... })
}
const options = {
context: { store },
childContextTypes: { store: React.PropTypes.object.isRequired }
}
const _wrapper = mount(<SampleComponent {...defaultProps} />, options)
这种 方式存在的问题是 实际上是伪造了一个context.store, 这个store的功能是不足以让程序去获取真正redux 里面的任何数据, 也无法存储任何数据。
所以不建议用这种方法
Testing with Jest and Webpack aliases
配置方式如下, 直接在jest.config.js 中添加如下配置就可以了:
"jest": {
"modulePaths": ["src"],
"moduleDirectories": ["node_modules"],
"moduleNameMapper": {
"^@shared$": "<rootDir>/shared/",
"^@components$": "<rootDir>/shared/components/"
}
},
如何略过redux
下面是来自于stack overflow 的解决方案
- React, Jest, Enzyme how to pass test App.js with store redux
This is happening because you're trying to test the component without a (which would normally make the store available to its sub-components).
What I normally do in this situation is test my component without the Redux connect binding. To do this, you can export the App component itself:
export class App extends Component // etc...
and then import that in the test file using the deconstructed assignment syntax:
import { App } from './App'
You can assume (hopefully... ;) ) that Redux and the React bindings have been properly tested by their creators, and spend your time on testing your own code instead.
关于一个测试报错的问题
直接上码说话
组件如下 JestAppTest.jsx
import React, { PureComponent } from 'react';
import { connect } from 'dva';
@connect()
class JestAppTest extends PureComponent {
render() {
return (
<div>
<p className="name">yanle</p>
<p className="name2">yanlele</p>
</div>
);
}
}
export default JestAppTest;
测试如下 index.test.js
import React from 'react';
import { shallow } from 'enzyme';
import PropTypes from 'prop-types';
import JestAppTest from './JestAppTest';
const store = {
subscribe: () => {
},
dispatch: () => {
},
getState: () => ({}),
};
const options = {
context: { store },
childContextTypes: { store: PropTypes.object.isRequired },
};
const indexComponent = shallow(<JestAppTest />, options);
describe('User', () => {
it('有title', (done) => {
expect(indexComponent.find('p').at(0).text()).toBe('yanle');
done();
});
});
执行命令, 总是会报错
Method “text” is meant to be run on 1 node. 0 found instead.
19 | describe('User', () => {
20 | it('有title', (done) => {
> 21 | expect(indexComponent.find('p').at(0).text()).toBe('yanle');
| ^
22 | done();
23 | });
24 | });
但是我这里明显就有这个p 标签, 然后却说找不到p标签
原因: 这个地方最重大的原因是因为 connect 本身也是一个高阶组件, 改变了上下文, 渲染方式 shallow和mount 的区别
shallow只渲染当前组件,只能能对当前组件做断言;
mount会渲染当前组件以及所有子组件,对所有子组件也可以做上述操作。
一般交互测试都会关心到子组件,我使用的都是mount。但是mount耗时更长,内存啥的也都占用的更多,
如果没必要操作和断言子组件,可以使用shallow。
所以上诉情况下, 直接把 shallow 渲染改为 mount 渲染即可。
dva单元测试
如果项目是用的dva做的模块管理如何解决store注入
存在的问题:我把dva作为独立模块加入到自己的项目里面做状态管理。
用enzyme+jest给react 做测试的时候,
如何处理 Invariant Violation: Could not find "store" in either the context or props of "Connect...." 的问题?
error console.log
Invariant Violation: Could not find "store" in either the context or props of "Connect(UserManagement)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(UserManagement)".
21 | const IndexComponent = app.start();
22 |
> 23 | const indexComponent = mount(<Index />);
| ^
24 | describe('User', () => {
25 | it('title', (done) => {
26 | expect(indexComponent
test code:
import React from 'react';
import { mount } from 'enzyme';
import Index from '../index';
const indexComponent = mount(<Index />);
describe('User', () => {
it('title', (done) => {
expect(indexComponent
.find('p')
.at(0)
.text()).toBe('yanle');
done();
});
});
这个问题的终极解决办法:
dva.setup.js
import dva from 'dva';
import browserHistory from 'history/createBrowserHistory';
import { routerRedux } from 'dva/router';
import createLoading from 'dva-loading';
import PropTypes from 'prop-types';
import globalErrorHandler from '../globalHandler';
import models from '../models';
const app = dva({
history: browserHistory(),
onError: globalErrorHandler,
extraReducers: {
router: routerRedux.routerReducer,
},
});
app.use(createLoading());
models.forEach(m => app.model(m));
app.router(() => ({}));
app.start();
const options = {
context: { store: app._store },
childContextTypes: { store: PropTypes.object.isRequired },
};
export default options;
测试代码:
import React from 'react';
import { mount } from 'enzyme';
import Index from '../index';
import options from '../test/dva.setup';
const wrapper = mount(<Index />, options);
describe('User', () => {
it('title', (done) => {
expect(wrapper
.find('.layout_header')
.at(0)
.text()).toBe('新建任务');
done();
});
it('has 4 ant-menu-item', () => {
expect(wrapper.find('.ant-menu-item').length).toBe(4);
});
it('table count', async (done) => {
await expect(wrapper.find('.ant-table-tbody').length).toBe(1);
done();
});
});
处理生命周期中的请求
很多请求接口是放置在componentDidMount等生命周期的, 然后通过接口请求, 异步渲染了组件。这个时候就涉及到我们需要模拟这个过程。
否则是拿不到异步渲染的结构的, 通过enzyme也是无法渲染出来。
异步action
异步action测试 可以稍微了解一下这个模块 redux-mock-store