Mocking Redux store when testing React components? - reactjs

I'm using React and Redux. I have a component which loads ChildComponent and depending on Redux's state will also load MainComponent
const ChooseIndex = ({ appInitMount }) => {
return (
<>
<ChildComponent />
{!appInitMount && <MainComponent />}
</>
);
};
const mapStateToProps = ({ main }) => {
return {
appInitMount: main.appInitMount
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseIndex);
I'm trying to write a test to check that ChildComponent is loaded:
import React from "react";
import { render } from "react-testing-library";
import ChooseIndex from "../choose-index";
test("ChooseIndex should call ChildComponent", () => {
const wrapper = render(
<ChooseIndex />
);
});
I get this error:
Error: Uncaught [Invariant Violation: Could not find "store" in either
the context or props of "Connect(ChooseIndex)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(ChooseIndex)".]
Should I mock Redux by passing an object literal to ChooseIndex? Or should I create a Redux store (as my real application does) for every test?

Try to render your component like this:
render(
<Provider store={store}>
<ChooseIndex />
</Provider>
)
And pass the actual store you use in your app. In this way, you're testing the real logic that you'll use in production. You also don't care what actions get dispatched and what's in the state. You look at what gets rendered and interact with the UI—which is what matters in the end.
Separating the component from Redux and testing the two in isolation is against the whole point of react-testing-library. You want to test your app as a real user would.

If you check out the writing tests section of the redux docs, there is an example of testing a connected component.
when you import it [A redux connected component], you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component
As with most unit tests, you really want to be testing your components, and not that redux is working correctly. So the solution for you is to export both the component and the connected component, while only testing the component itself, and providing whatever props redux is passing to your component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)

Related

createContext hook not working for react components inside JSP

I am rendering my react component inside an existing JSP page using
ReactDOM.render(
React.createElement(MyReactComponents.myReactComponent, {
props
}),
document.querySelector("#id")
);
and the react component is as follows:
import MyStore from "./MyStore";
const MyReactComponent: React.FC<any> = (props: any) => {
const store = useContext(MyStore);
store.myFunction();
---code---
}
and MyStore is as follows:
export class MyStore{
---Code---
}
export default createContext(new MyStore());
But i'm getting this error:
And one more importing thing to notice is that when I'm trying to render this react component on top of another existing react component, i'm not getting any error and everything is working fine.
Can someone please explain me what might be causing the issue?
I'm not sure, but maybe you are misusing the useContext hook?
Whenever you use it inside a component Child, then at least one of its parent component must call the <Context>.Provider, so that it is initialized down the tree.
In your example, you render MyReactComponent using ReactDOM.render: due this, I suppose MyReactComponent is the first component in your tree. If that is the case, when you use useContext inside it, it cannot find any MyStore context.
So, probably, you just need to wrap your MyReactComponent with a context provider.
export class MyStore { ... }
export const MyStoreContext = createContext(new MyStore());
---
ReactDOM.render(
<MyStoreContext.Provider>
<MyReactComponent {...props />
</MyStoreContext.Provider>
, document.querySelector("#id"));
And then, inside MyReactComponent, you can use const store = useContext(MyStoreContext);.

Jest mocking return value of a static method of connected component

I am unit testing my React app with jest and enzyme and all is good until I happened to test a class component connected with Redux. I went ahead and tested it by wrapping the component with a and then shallow mounting it. However I am not able to mock return value of the static methods defined in my connected component.
Below is my sample code:
const wrapper = shallow(
<Provider store={store}>
<TestComponent {...props} />
</Provider>
).dive();
Now when I try to mock the return value of static method defined in TestComponent, it doesn't have any effect, it fails:
TestComponent.myStaticMethod = jest.fn().mockReturnValue(true);
Interesting part is, if I export the unconnected component as also suggested in redux docs, the above mock syntax work well. Is it fine to export the unconnected component just for the purpose of testing? I tried mocking before the shallow and then after it, both ways didn't work.
connect returns a new component so you cannot override the static methods of the wrapped component in this way, (which is not a good idea like Andrew said)
But assuming this is what you want to do, you can mock the connect function from react-redux to return the original component
then pass all the props it gets from mapStateToProps and mapDispatchToProps as a standard React props
import MyComponent from './MyComponent';
jest.mock('react-redux' () => ({
connect: () => Component => Component
}))
MyComponent.myStaticMethod = jest.fn().mockReturnValue(true);
shallow(<MyComponent {...props}/>)

Error while doing unit test in react-redux connected component

I am trying to test the connected component(react-redux) with jest-enzyme. I am using react-redux-mock store. When I run my test to find one div in the component it gives me this error.
Invariant Violation: Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: <Provider context={MyContext}><ConnectedComponent context={MyContext} /></Provider>. You may also pass a {context : MyContext} option to connect
I did mount and tested just component without redux it works but I want to do a > shallow test.
describe("Input Component", () => {
let wrapper;
let store;
beforeEach(() => {
store = mockStore(initialState);
wrapper = shallow(<Input store={store} />);
});
it("should rendder without error", () => {
expect(wrapper.find("div")).toHaveLength(1);
});
});
How do you import your component?
if your are importing it with import App from './yourpath/App' for example, ou're actually holding the wrapper component returned by connect(), and not the App component itself.
In order to be able to test the App component itself without having to deal with the decorator, you must to also export the undecorated component:
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
And import it in your test file like that:
import { App } from './yourpath/App'

How to access store in second component in react-redux

I have a single component App.js where I trying to save state using redux. In index.js where I set store for only <App /> component.
index.js
let store = createStore(scoreReducer);
ReactDOM.render(
<Provider store={store}><App /></Provider>,
document.getElementById("root")
);
registerServiceWorker();
I have this method in App.js to map state to props which is available inside App.js.
App.js
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
Everything is well so far, now I am not sure how to access { this.props.score} in another component ?
What changes I need to do in index.js and second component if I want to access {this.props.score} in another component ?
When you are using Provider any component that is children of the Provider Higher Order Component can access the store properties though the use of connect function.
So you can add the following in any component that is a child of Provider and access the score prop
function mapStateToProps(state) {
return { score: state.score, status: state.status };
}
export default connect(mapStateToProps)(MyComponent)
However if this other component is a direct child of App component then you can also pass the score prop as a prop to this component from App like
<MyComponent score={this.props.score}/>
Provider component sets the context for all its children, providing the store in it. when you use the High Order Component(HOC) connect you can wrap any component and access the store through the provided mapStateToProps and mapStateToProps no matter how nested they are. You can also access the store using context context.store but this is not recommended. Using map functions and connect, similar to what you have with your App component, is the best approach.

react-redux: how do you access the store from 'anywhere' without importing it?

In the react-redux documentation it states that when using React-redux and connect() importing the store is not recommended. It's an anti-pattern.
http://redux.js.org/docs/faq/StoreSetup.html
Similarly, while you can reference your store instance by importing it
directly, this is not a recommended pattern in Redux. If you create a
store instance and export it from a module, it will become a
singleton. This means it will be harder to isolate a Redux app as a
component of a larger app, if this is ever necessary, or to enable
server rendering, because on the server you want to create separate
store instances for every request.
With React Redux, the wrapper classes generated by the connect()
function do actually look for props.store if it exists, but it's best
if you wrap your root component in and let
React Redux worry about passing the store down. This way components
don't need to worry about importing a store module, and isolating a
Redux app or enabling server rendering is much easier to do later.
How, then, do I access the store from any component of my choosing(even deep down in the application) once I've properly wired in the store to my app? My code properly connects App but I can't get access to the store from any child components at all. store.dispatch() is null, store.getState() is null, etc. I feel that the documentation is lacking in this regard. It's said to be magic but I'd like to know how to use the magic. Do I need to write mapDispatchToProps() again and again for every single container component? A use case would be the currentUser prop that would be available to every single child component in the application. I'd like to pass that down from App to every single child.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);//App is now a connected component, that part is working
Inside App I have a Login component, and I'd like to dispatch an action inside it. But I need a reference to store, but apparently I'm not supposed to import it.
This is where the concept of containers come into play.
Suppose you wanted to render a Login component inside of your App. You will make a connected container.
Step 1 is to create a simple action:
const LOGIN_ATTEMPT = 'auth/LOGIN_ATTEMPT';
export const login = name => ({
type: LOGIN_ATTEMPT,
payload: { name },
});
You will now use react-redux in order to connect this action to your "presentational component". This will come through to the component as a prop.
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { login } from 'actions/auth'; // your action
import Login from 'components/auth/Login'; // your component to connect it to.
// state refers to the "current state" of your store.
const mapStateToProps = state => ({ currentUser: state.auth.user });
// dispatch refers to `store.dispatch`
const mapDispatchToProps = dispatch => {
// calling this is like calling `store.dispatch(login(...params))`
login: bindActionCreators(login, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
This connect function will take these two functions as parameters. You can now import this connected container and use it with the functions "bound" to it as properties.
Example component below.
export default class Login extends Component {
static propTypes = {
// properties below come from connect function above.
currentUser: PropTypes.shape({
name: PropTypes.string.isRequired,
}).isRequired,
login: PropTypes.func.isRequired,
};
state = { name: "" };
onChange = ({ target: { value: name } }) => this.setState({ name });
onSubmit = e => {
e.preventDefault();
login(this.state.name);
};
render() {
return (
<form onSubmit={this.onSubmit}>
<input
placeholder="name"
onChange={this.onChange}
value={this.state.name}
/>
</form>
);
}
}
Notice, you never had to reference the store.

Resources