javascript - 为反应组件编写 Jest 测试用例时出错:“初始化前无法访问‘initialState’”
问题描述
我正在使用 jest 为我的一个反应组件 ProductDetailsForm.js 编写测试用例,其中我遇到了以下错误。
FAIL src/components/__tests__/ProductDetailsForm.test.js
Test suite failed to run
ReferenceError: Cannot access 'initialState' before initialization
34 |
35 | export default function reducer(state, action = {}) {
> 36 | state = state || initialState;
| ^
37 |
38 | switch (action.type) {
39 | case AppDucksActionTypes.App_DUCKS_TOGGLE_SIDEBAR: {
at reducer (src/redux/ducks/AppDucks.js:36:22)
at node_modules/redux/lib/redux.js:372:24
at Array.forEach (<anonymous>)
at assertReducerShape (node_modules/redux/lib/redux.js:370:25)
at combineReducers (node_modules/redux/lib/redux.js:435:5)
at Object.<anonymous> (src/redux/rootReducer.js:20:16)
at Object.<anonymous> (src/redux/store/createStore.js:4:1)
at Object.<anonymous> (src/redux/services/HttpService.js:2:1)
at Object.<anonymous> (src/redux/services/AppService.js:2:1)
at Object.<anonymous> (src/redux/ducks/AppDucks.js:3:1)
at Object.<anonymous> (src/components/ProductDetailsForm.js:3:1)
at Object.<anonymous> (src/components/__tests__/ProductDetailsForm.test.js:3:1)
经过大量的代码挖掘后,我发现这可能是因为 createStore.js 中的某些东西(redux store 已在其中创建)
我是怎么得出结论的?
我在 HttpService.js (在第 2 行)中注释掉了从 createStore.js 的导入,并且测试通过且没有错误
以下是不同文件的代码供您参考。这些文件是按使用顺序提到的(比如 createStore.js 被导入到 HttpService.js 中,HttpService.js 被导入到 AppService.js 中等等)。
createStore.js
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from '../rootReducer';
import storage from 'redux-persist/lib/storage';
const initialState = {};
const middleWares = [];
const composeEnhancers = process.env.NODE_ENV !== 'production' &&
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
}) : compose;
const composedEnhancers = composeEnhancers(
applyMiddleware(...middleWares)
);
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2
};
const pReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(
pReducer,
initialState,
composedEnhancers
);
const persistor = persistStore(store);
export {
store,
persistor
};
HttpService.js
import axios from 'axios';
import {store} from '../store/createStore';
import {showErrors} from '../ducks/AppDucks';
axios.interceptors.response.use(response => {
const {dispatch} = store;
const errorHeader = response.headers['x-errors'];
if (errorHeader) {
const errors = JSON.parse(errorHeader);
dispatch(showErrors(dispatch, errors));
}
return response;
});
export function get(uri, config = {}, responseType) {
if (responseType) {
config.responseType = responseType;
}
return axios.get(uri, config);
}
export function post(uri, data) {
return axios.post(uri, data);
}
export default {
get,
post
}
应用服务.js
import API_ROOT from './URI';
import HttpService from './HttpService';
export const fetchApiVersion = () => {
let url = `${API_ROOT}/version`;
let encodedURL = encodeURI(url);
return HttpService
.get(encodedURL)
.then(res => res.data);
};
AppDucks.js
import keymirror from 'keymirror';
import { createAction } from 'redux-actions';
import { fetchApiVersion } from '../services/AppService';
import packageJson from '../../../package.json';
export const initialState = {
ui: {
sidebar: {
showSidebar: true,
expandedProductLineMap: {},
selectedUploadType: ''
}
}
}
const AppDucksActionTypes = keymirror({
App_DUCKS_GET_API_VERSION: null,
App_DUCKS_TOGGLE_SIDEBAR: null
});
export const toggleSidebar = createAction(AppDucksActionTypes.App_DUCKS_TOGGLE_SIDEBAR);
export const getApiVersion = createAction(AppDucksActionTypes.App_DUCKS_GET_API_VERSION,
dispatch => {
fetchApiVersion()
.then(data => {
let uiVersion = prepareVersion(packageJson.version);
let apiVersion = prepareVersion(data);
if (uiVersion !== apiVersion) {
dispatch(blockUi({ blocked: true, errorCode: 1000 }));
}
});
}
);
export default function reducer(state, action = {}) {
state = state || initialState;
switch (action.type) {
case AppDucksActionTypes.App_DUCKS_TOGGLE_SIDEBAR: {
return {
...state,
ui: {
...state.ui,
sidebar: {
...state.ui.sidebar,
showSidebar: !state.ui.sidebar.showSidebar
}
}
}
}
default:
return {
...state
};
}
}
ProductDetailsForm.js
import React, {Component} from "react";
import {connect} from "react-redux";
import { toggleSidebar } from "../../redux/ducks/AppDucks";
import XMLMetaDefinition from "../XMLMetaDefinition";
import MainView from "../layout/main-view/MainView";
export class ProductDetailsForm extends Component {
state = {
xmlVersion: '',
error: ''
};
componentDidMount() {
if (this.props.showSidebar) {
this.props.toggleSidebar();
}
}
handleXMLMetaDefinitionInput = e => {
const {value, name} = e.target;
if (name === "xmlVersion") {
this.checkSpecialCharacters(value);
}
};
render() {
return (
<div className="container">
<MainView heading='Add meta data definition:'>
<XMLMetaDefinition
readOnly={false}
xmlMetaData={this.state}
handleXMLMetaDefinitionInput={
this.handleXMLMetaDefinitionInput
}
/>
</MainView>
</div>
);
}
}
const mapDispatch = dispatch => {
return {
toggleSidebar: () => dispatch(toggleSidebar(dispatch))
};
};
const mapState = state => {
return {
showSidebar: state.app_reducer.ui.sidebar.showSidebar
};
};
export default connect(
mapState,
mapDispatch
)(ProductDetailsForm);
ProductDetailsForm.test.js
import React from "react";
import { shallow } from "enzyme";
import { ProductDetailsForm } from "../ProductDetailsForm";
let clearAll, setProductDetailsFormValue, history, wrapper;
beforeEach(() => {
clearAll = jest.fn();
history = { push: jest.fn() };
setProductDetailsFormValue = jest.fn();
wrapper = shallow(
<ProductDetailsForm
clearAll={clearAll}
history={history}
setProductDetailsFormValue={setProductDetailsFormValue}
/>
);
});
afterEach(() => {
jest.clearAllMocks();
});
test("should call handleMetaDefinitionInput", () => {
const targetObj = { target: { name: "xmlVersion", value: "!abc?" } };
const component = wrapper.find("XMLMetaDefinition");
component.props().handleXMLMetaDefinitionInput(targetObj);
});
解决方案
推荐阅读
- javascript - 如何从javascript中的对象内部解构数组?
- r - 使用 dplyr 分组、连接和返回 3 列
- azure - 可以使用 powershell 删除 Azure 自动化更新管理计划,但它们仍然存在于门户中
- javascript - 为什么这个 addEventListener('click', function) 需要两次而不是一次?
- java - 如何在 JSP 文件中为导入的包设置别名?
- ios - UITextField 不根据指定的宽度缩放
- xmpp - ejabberd BOSH 搜索一无所获
- unit-testing - 用 Jest 模拟 Flow.js 接口?
- javascript - 用 useEffect 替换 getDerivedStateFromProps
- r - 仅选择具有特定时差的相关行