'React/Redux Testing - Could not find "store" in either the context or props
I'm new to react/redux and just getting into testing my first app with chai. I'm using redux-form/immutable and react-router, and I'm not sure how to resolve this issue when testing:
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))".
I found this issue: https://github.com/reactjs/react-redux/issues/57 which seems like the same problem but the solution of adding a function that returns the children to the Router element doesn't fix the problem.
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);
});
});
Fix
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>
);
Solution 1:[1]
LoginContainer is a connected component connect(mapStateToProps)(LoginContainer)
. That means it depends on the Redux store to generate the subtree, and when testing you don't have the wrapping <Provider store={store} />
around it.
The solution is to use redux-mock-store
:
import configureStore from 'redux-mock-store';
const mockStore = configureStore();
const component = renderer.create(
<Provider store={mockStore()}>
<LoginContainer />
</Provider>
);
Solution 2:[2]
when you connect a component, the connect
function tries to find Provider with store in the parent hierarchy. If it does not find it it throws that error.
Easy solution is to wrap each testing component with Provider.
import reducers from "reducerPath" import {Provider} from "react-redux" import {createStore} from "redux" <Provider store={createStore(reducers,{})} > <LoginComponent/> </Provider>
this will solve the issue but it is expensive because you have to set this up for each component
Easy way is to change the way how you render your app. Instead of this in
index.jsx
:ReactDOM.render( <Provider store={store}> <Router history={hashHistory}>{configRoutes()}</Router> </Provider>, document.getElementById('app') );
First create a wrapper component:
import reducers from "reducerPath"
import {Provider} from "react-redux"
import {createStore} from "redux"
export default (props)=>{
<Provider store={createStore(reducers,{})}>
{props.children}
</Provider>
}
then index.jsx
import Wrapper from "./wrapper.jsx"
ReactDOM.render(
<Wrapper>
<Router history={hashHistory}>{configRoutes()}</Router>
</Wrapper>,
document.getElementById('app')
);
Now in testing, you can use this Wrapper
component to wrap the testing component instead of depending anoter third party library:
const component = renderIntoDocument(
<Wrapper store={store}>
<LoginContainer />
</Wrapper>
);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Alk |
Solution 2 | Yilmaz |