When migrating to redux we need to ensure that each component that relies on redux has access to the redux store. To do that we use the <Provider>
component. We can use that in each individual unit test or in some kind of wrapper.
This lesson demonstrates both approaches and ends with creating our own version or @testing-library/react
s render
method, that will automatically wrap all components in a <Provider>
with our redux store.
More information about this approach can be found here: https://redux.js.org/recipes/writing-tests#connected-components
If you're using Enzyme you may find the mountWithStore utility from enzyme-redux serves a similar purpose.
Note:
The reduxRender
function shown in this lesson actually includes a typo, based on where the options
are spread. The correct version should look like:
const reduxRender = (ui, options) =>
render(ui, { wrapper: ReduxProvider, ...options });
Jamund Ferguson: [0:00] Go ahead and open up tests/ExchangeRate.test.js. In my terminal, I'm going to type yarn test. I'm going to go ahead and filter just for this one particular test, ExchangeRate.test.js. As I run that, you'll notice that it says, "Uncaught [Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>] ."
[0:23] You remember when we created our index.js file, we had to wrap it in a Provider. The Provider uses React's Context API to make sure that every component inside of it has access to this store that we pass in, so then when I call things like useSelector or useDispatch, it knows which Redux store that it should interact with.
[0:45] Back here in our ExchangeRate.test.js file, I don't wrap anything in Provider. Let's do that now. First, manually. Type import { Provider } from "react-redux" and import { store } from "../../store/store". A little bit of redundancy there. Then wrap this in <Provider store={store}> and then wrap that all up.
[1:15] With the Provider, my test now has all of the information needed to run this extremely simple test. It doesn't give any errors. In fact, it passes.
[1:24] However, what about all the other tests that we have? I go back to run all tests. You can see that three out of four are failing now for the same issue. How do we solve this problem on a slightly larger scale? If you're using testing-library/React, which we are, there's a fairly easy way to go about doing this.
[1:40] Create a file in your src folder or wherever you'd like called tests-utils.js. Let's take with us, the Provider and store from the test that we had just modified. We'll import those at the top and make sure that we import { store } from "./store/store". We also need to import { render } from "@testing-library/react".
[2:04] We're going to create a component called ReduxProvider, function ReduxProvider(). It's going to take only children, and it will return <Provider store={store}>{children}</Provider>. A little bit weird, but this is something that we're going to pass into a special override version of testing-library's render function.
[2:27] Now type const reduxRender =...This is going to be an arrow function that takes two arguments, ui and options and then render(ui, { wrapper: -- We'll pass in our new function -- ReduxProvider } and ...options. We're almost done here. Now, we have to essentially re-export everything from the existing testing-library.
[2:53] Export * from "@testing-library/React". Finally, we will export our new render function. Export { reduxRrender as render }. This is a little bit wonky here, kind of hard to follow, but essentially, we've created a file called test-utils.js, and inside of it, we're exporting the existing testing-library/React, the same testing library we're using inside of our tests to render our components.
[3:23] But now, instead of using the built-in render function inside of testing-library/React, we're going to change our test to use ../../test-utils, which will automatically wrap whatever components we pass into render in our provider, so they'll all automatically get Redux.
[3:41] If we update all of our tests to use this new path, just copy that in there, copy that in there, save, copy that in there and save, you'll see that they now all pass.