Test React-Redux with mock context and actions - reactjs

How can I test a Reaect-Redux component and mock its context and actions?
With this, it can't find any actions:
const wrapper = mount(
<S437TransactionAuth
store={store}
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>,
mockContext
);
This can't find mock actions or context
const wrapper = mount(
<Provider store={store}>
<S437TransactionAuth
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>
</Provider>,
mockContext
);
What is the standard practice? I'm using Mocha, for reasons beyond my control.

Our standard practice is to export both the connect component as default and the component itself as a named export.
connect is already tested for you so there's no point in verifying it's actually passing stuff from the Redux store to your component. What you can test is that your component uses that props correctly, which you can mock in your non-connected component.
You can also test your selectors and action creators you assign in mapStateToProps and mapDispatchToProps in isolation.
Last but not least, you can always simply configure your real store for the test (inside your describecalls!) and wrap your subject in a Provider with the real store. But this path could lead to you having to dispatch some actions to setup the state as your component expects it (like mocking an API call to have data, and so on).

Assuming you are using enzyme for doing the mounting, the context is supposed to be passed as
const wrapper = mount(
<S437TransactionAuth
// I'm assuming you have put your store in the context so I've removed store={store} for here
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>,
{ context: mockContext }
);
I've personally just used the react-redux Provider without doing anything with the second parameter of mount, like so
const wrapper = mount(
<Provider store={store}>
<S437TransactionAuth
s437updateGui="{mock.action.s437updateGUI}"
setCurrentUserId="{mock.action.setCurrentUserId}"
/>
</Provider>
);
Also, if you're not already using it, redux-mock-store makes testing connected components much easier but allowing you to set up an initial state to render, assert expected render in the component, or simulate user actions on the component with enzyme, and assert the expected actions against the mock store.

Related

Testing a Redux connected component with enzyme and jest

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 run assertions after updating the props of component, nor I am able to spy on the class methods being called or not.
Below is my sample code:
const wrapper = shallow(
<Provider store={store}>
<TestComponent {...props} />
</Provider>
).dive();
Now when I try t set the props of the wrapper and run assertions, it fails:
wrapper.setProps({
showHeader: true
})
expect(wrapper.find('h1')).toHaveLength(1);
Interesting part is, if I export the unconnected component as also suggested in redux docs, the assertions work well. Is it fine to export the unconnected component just for the purpose of testing? I can't spy on instance methods/lifecycle methods if I try to test the connected component.
You can mock the connect function from react-redux to return the original component
jest.mock('react-redux', () => ({
connect: () => Component => Component
}))

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}/>)

Using React-Redux: How to Access Variables in the Store, within Nested Components

I am using React-Redux, but I am not able to figure out how to access a variable in the Redux store inside of my nested components.
How can I share a variable between components, using React-Redux?
For example:
I have an 'index.js' file and 30 nested components. Managing these components becomes difficult after a while.
I have a 'C1.js' component. Let's just say I wrote this code in it.
function Reducer(state = 'example' , action) {
return state;
}
const store = createStore(Reducer)
index.js file:
<Provider store = {store}>
<App/>, document.getElementById('root')
</Provider>
How do I pass the 'store' variable to the 'C1.js' component to the index.js file?
Thanks...
You need to use something called "Connect" to connect your various components to the provider.
In the file that contains your C1.js component:
import {connect} from 'react-redux'
const MyComponent = () => {
let someData = props.someData
return(
//all of your JSX for your component here
)
}
const mapState = state => {
return {
someData: state.someData
}
}
export default connect(mapState)(MyComponent)
In the code above, notice the mapStateFunction. Connect is hooking that up with the Provider, and the state that is on the Provider. So that is where you are able to link whatever properties are on your Provider (React-Redux) state with this particular data.
Now, in your component, you will now have prop.someData
-
In the index file, you have your Provider in the wrong place, you need to change your code to this:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
See, the difference there? The is the React Element (and all of its children that you are asking React to render to the DOM). It is the first parameter of the ReactDOM.render function.
The second parameter to the ReactDom.render function is the element in the DOM where you want it to put all of your React elements.
You did not configure well redux and react. You need to go over the doc of redux to setup correctly. Should get working after that.

React-Redux: Why do we need to wrap the component with Provider?

The best practice for using Redux in React application is wrapping the component in a 'Provider' component:
const rootElement = document.getElementById('root')
ReactDOM.render(
<Provider store={store}>
<TodoApp />
</Provider>,
rootElement
)
You can see it in React-Redux documentation: https://react-redux.js.org/introduction/basic-tutorial.
What is the benefit we get from this attitude?
Why not just importing the 'store' inside the 'ToDoApp' component and access 'store' as an imported variable? For example:
import { store } from './store';
class TodoApp extends Component {
constructor(props) {
super(props);
console.log('constructor')
}
render() {
console.log(store.getState());
}
}
The actual point that is happening in the redux, when we are calling the provider: that it is having the store of all the states and the provider does the job to connect the component with the redux or simply you can say that the provider does the job to connect your app with the redux as the author of the redux has not only to design the library for a single framework, it would have so many uses on different platforms, the store is having two things inside (reducers and state) and all the states get an outer layer of provider which connects the app with the redux library.
This is very important to the way react-redux works.
When you use connect over your component, it attempts to get the store from the Provider you set, using React's context mechanism.
It is highly unlikely that you will use Redux in React without using connect, so I would advise that you keep it there.

Mocking Redux store when testing React components?

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)

Resources