React/Redux 测试 - 在上下文或道具中找不到 "store"
React/Redux Testing - Could not find "store" in either the context or props
我是 react/redux 的新手,刚刚开始使用 chai 测试我的第一个应用程序。我正在使用 redux-form/immutable 和 react-router,我不确定在测试时如何解决这个问题:
1) Login
renders a login form:
Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(LoginForm))". Either wrap the root component in a <Provider>, or explicitly pass "st
ore" as a prop to "Connect(Form(LoginForm))".
我发现了这个问题:https://github.com/reactjs/react-redux/issues/57 这似乎是同一个问题,但是将 returns 子元素添加到 Router 元素的函数的解决方案并没有解决问题。
index.jsx
import {configRoutes} from './config'
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>{configRoutes()}</Router>
</Provider>,
document.getElementById('app')
);
config.js
import App from './App';
import {PackageStoreContainer} from './components/Package/PackageContainer';
import {LoginStoreContainer} from './components/Login/LoginContainer';
export function configRoutes() {
const routes = <Route component={App}>
<Route path="/packages" component={PackageStoreContainer} />
<Route path="/" component={LoginStoreContainer} />
</Route>;
return routes;
}
LoginContainer.jsx
export class LoginContainer extends React.Component {
constructor(props, context) {
super(props, context);
sessionStorage.removeItem('credentials');
}
submit = values => {
this.props.dispatch(loginUser(values));
}
render() {
return (
<div className="row">
<LoginForm onSubmit={this.submit} />
</div>
);
}
};
const mapStateToProps = (state, ownProps) => {
return {
creds: {}
};
}
export const LoginStoreContainer = connect(mapStateToProps)(LoginContainer);
LoginForm.jsx
const required = value => (value ? undefined : 'Required');
const renderField = ({input, label, type, meta: {touched, error, warning}}) => (
<div>
<ControlLabel htmlFor="email">{label}</ControlLabel>
<div>
<input {...input} placeholder={label} type={type} className="form-control" />
{touched &&
((error && <span>{error}</span>) ||
(warning && <span>{warning}</span>))}
</div>
</div>
);
const LoginForm = (props) => {
const { handleSubmit } = props
return (
<Col xs={12} md={12}>
<form onSubmit={handleSubmit}>
<FormGroup>
<Field
name="username"
type="email"
component={renderField}
label="Username"
validate={[required]}
/>
</FormGroup>
<FormGroup>
<Field
name="password"
type="password"
component={renderField}
label="Password"
validate={[required]} />
</FormGroup>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</Col>
);
};
export default reduxForm({
// a unique name for the form
form: 'login'
})(LoginForm)
Login_spec.js
describe('Login', () => {
it('renders a login form', () => {
const component = renderIntoDocument(
<LoginContainer />
);
const fields = scryRenderedDOMComponentsWithTag(component, 'input');
const submit = scryRenderedDOMComponentsWithTag(component, 'button');
// expect(fields.length).to.equal(3);
// expect(submit.length).to.equal(1);
});
});
修复
import {Provider} from 'react-redux';
const createMockStore = (getState) => {
const middlewares = [];
return configureStore(middlewares)(getState);
};
const store = createMockStore();
const component = renderIntoDocument(
<Provider store={store}>
<LoginContainer />
</Provider>
);
LoginContainer 是一个连接组件connect(mapStateToProps)(LoginContainer)
。这意味着它依赖于 Redux 存储来生成子树,并且在测试时你没有围绕它的包装 <Provider store={store} />
。
解决方法是使用redux-mock-store
:
import configureStore from 'redux-mock-store';
const mockStore = configureStore();
const component = renderer.create(
<Provider store={mockStore()}>
<LoginContainer />
</Provider>
);
当您连接组件时,connect
函数会尝试在父层次结构中查找具有存储的提供程序。如果找不到它,则会抛出该错误。
简单的解决方案是用 Provider 包装每个测试组件。
import reducers from "reducerPath"
import {Provider} from "react-redux"
import {createStore} from "redux"
<Provider store={createStore(reducers,{})} >
<LoginComponent/>
</Provider>
这将解决问题,但它很昂贵,因为您必须为每个组件设置它
简单的方法是更改呈现应用程序的方式。而不是 index.jsx
中的这个:
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>{configRoutes()}</Router>
</Provider>,
document.getElementById('app')
);
首先创建一个包装器组件:
import reducers from "reducerPath"
import {Provider} from "react-redux"
import {createStore} from "redux"
export default (props)=>{
<Provider store={createStore(reducers,{})}>
{props.children}
</Provider>
}
然后index.jsx
import Wrapper from "./wrapper.jsx"
ReactDOM.render(
<Wrapper>
<Router history={hashHistory}>{configRoutes()}</Router>
</Wrapper>,
document.getElementById('app')
);
现在在测试中,您可以使用这个 Wrapper
组件来包装测试组件,而不是依赖于另一个第三方库:
const component = renderIntoDocument(
<Wrapper store={store}>
<LoginContainer />
</Wrapper>
);
我是 react/redux 的新手,刚刚开始使用 chai 测试我的第一个应用程序。我正在使用 redux-form/immutable 和 react-router,我不确定在测试时如何解决这个问题:
1) Login
renders a login form:
Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(LoginForm))". Either wrap the root component in a <Provider>, or explicitly pass "st
ore" as a prop to "Connect(Form(LoginForm))".
我发现了这个问题:https://github.com/reactjs/react-redux/issues/57 这似乎是同一个问题,但是将 returns 子元素添加到 Router 元素的函数的解决方案并没有解决问题。
index.jsx
import {configRoutes} from './config'
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>{configRoutes()}</Router>
</Provider>,
document.getElementById('app')
);
config.js
import App from './App';
import {PackageStoreContainer} from './components/Package/PackageContainer';
import {LoginStoreContainer} from './components/Login/LoginContainer';
export function configRoutes() {
const routes = <Route component={App}>
<Route path="/packages" component={PackageStoreContainer} />
<Route path="/" component={LoginStoreContainer} />
</Route>;
return routes;
}
LoginContainer.jsx
export class LoginContainer extends React.Component {
constructor(props, context) {
super(props, context);
sessionStorage.removeItem('credentials');
}
submit = values => {
this.props.dispatch(loginUser(values));
}
render() {
return (
<div className="row">
<LoginForm onSubmit={this.submit} />
</div>
);
}
};
const mapStateToProps = (state, ownProps) => {
return {
creds: {}
};
}
export const LoginStoreContainer = connect(mapStateToProps)(LoginContainer);
LoginForm.jsx
const required = value => (value ? undefined : 'Required');
const renderField = ({input, label, type, meta: {touched, error, warning}}) => (
<div>
<ControlLabel htmlFor="email">{label}</ControlLabel>
<div>
<input {...input} placeholder={label} type={type} className="form-control" />
{touched &&
((error && <span>{error}</span>) ||
(warning && <span>{warning}</span>))}
</div>
</div>
);
const LoginForm = (props) => {
const { handleSubmit } = props
return (
<Col xs={12} md={12}>
<form onSubmit={handleSubmit}>
<FormGroup>
<Field
name="username"
type="email"
component={renderField}
label="Username"
validate={[required]}
/>
</FormGroup>
<FormGroup>
<Field
name="password"
type="password"
component={renderField}
label="Password"
validate={[required]} />
</FormGroup>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</Col>
);
};
export default reduxForm({
// a unique name for the form
form: 'login'
})(LoginForm)
Login_spec.js
describe('Login', () => {
it('renders a login form', () => {
const component = renderIntoDocument(
<LoginContainer />
);
const fields = scryRenderedDOMComponentsWithTag(component, 'input');
const submit = scryRenderedDOMComponentsWithTag(component, 'button');
// expect(fields.length).to.equal(3);
// expect(submit.length).to.equal(1);
});
});
修复
import {Provider} from 'react-redux';
const createMockStore = (getState) => {
const middlewares = [];
return configureStore(middlewares)(getState);
};
const store = createMockStore();
const component = renderIntoDocument(
<Provider store={store}>
<LoginContainer />
</Provider>
);
LoginContainer 是一个连接组件connect(mapStateToProps)(LoginContainer)
。这意味着它依赖于 Redux 存储来生成子树,并且在测试时你没有围绕它的包装 <Provider store={store} />
。
解决方法是使用redux-mock-store
:
import configureStore from 'redux-mock-store';
const mockStore = configureStore();
const component = renderer.create(
<Provider store={mockStore()}>
<LoginContainer />
</Provider>
);
当您连接组件时,connect
函数会尝试在父层次结构中查找具有存储的提供程序。如果找不到它,则会抛出该错误。
简单的解决方案是用 Provider 包装每个测试组件。
import reducers from "reducerPath" import {Provider} from "react-redux" import {createStore} from "redux" <Provider store={createStore(reducers,{})} > <LoginComponent/> </Provider>
这将解决问题,但它很昂贵,因为您必须为每个组件设置它
简单的方法是更改呈现应用程序的方式。而不是
index.jsx
中的这个:ReactDOM.render( <Provider store={store}> <Router history={hashHistory}>{configRoutes()}</Router> </Provider>, document.getElementById('app') );
首先创建一个包装器组件:
import reducers from "reducerPath"
import {Provider} from "react-redux"
import {createStore} from "redux"
export default (props)=>{
<Provider store={createStore(reducers,{})}>
{props.children}
</Provider>
}
然后index.jsx
import Wrapper from "./wrapper.jsx"
ReactDOM.render(
<Wrapper>
<Router history={hashHistory}>{configRoutes()}</Router>
</Wrapper>,
document.getElementById('app')
);
现在在测试中,您可以使用这个 Wrapper
组件来包装测试组件,而不是依赖于另一个第三方库:
const component = renderIntoDocument(
<Wrapper store={store}>
<LoginContainer />
</Wrapper>
);