If you’re reading this, I’m guessing you’re testing a Redux app with Testing Library. And you probably want some tests to start with the Redux store in a particular state as the initial testing conditions.
As you probably know, Testing Library emphasizes “testing behavior” (tests that interact with your app the way users would). Behavioral testing purists would say: to set up a Redux store with certain values, start the test by running through user interactions that populate the state.
However, that’s simply not practical to do for every test, especially if the desired state needs a lot of interactions (and possibly server values) for setup. This blog post details how to set up a store factory to generate a test store (with initial values) for test setup.
Creating a Store Factory
The idea here is, you have a “factory function” to create a new store. This function creates the store for both production and tests to make sure your tests are as close as possible to production code.
Examples
Here’s an example of a store factory function using Redux Toolkit and Redux Saga:
import {
Action,
configureStore,
EnhancedStore,
ThunkAction,
} from "@reduxjs/toolkit";
import createSagaMiddleware from "redux-saga";
export const createStoreWithMiddlewares = (
initialState = {}
): EnhancedStore => {
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({ YOUR REDUCERS HERE },
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(sagaMiddleware).concat(YOUR MIDDLEWARES HERE),
preloadedState: initialState,
});
sagaMiddleware.run(YOUR ROOT SAGA);
return store;
};
Here’s another one using Redux and Redux Thunk:
import { createStore, applyMiddleware, Store } from "redux";
import ReduxThunk from 'redux-thunk';
export const middlewares = [ReduxThunk];
export const createStoreWithMiddlewares = (initialState = {}): Store => {
return createStore(
YOUR REDUCERS HERE,
initialState,
applyMiddleware(...middlewares)
);
};
Both of these store factories have a createStoreWithMiddlewares function that takes an initialState and can be used to create either the production store or a test store — with the same configuration. You can use these examples to write a createStoreWithMiddlewares for your app.
Using the Store Factory: Production
The production store can be created using createStoreWithMiddlewares either in your store/index.js file or src/index.js, and added as the store prop to the Redux Provider.
It’s very important to add the Redux Provider in src/index.js and not in App.js! If App.js contains the Redux Provider with the production store, then you won’t be able to test the App component with your test store, since the actual production store will be used when you render <App />.
Using the Store Factory: Tests
Now that the production Redux Provider has been relegated to index.js, we have total control over the store for our tests. Follow these steps and delight in the power!
Step 1: Create a Custom Render Function
We can overwrite the Testing Library [render](https://testing-library.com/docs/react-testing-library/api#render) function with a custom render that includes a Redux Provider with a private store just for that test. Write this code in, say, src/test-utils/index.tsx (actual file location and name are not important. Also, if you’re not using Typescript, you’ll probably want to use index.jsx instead of index.tsx).
import { EnhancedStore } from "@reduxjs/toolkit"; // for redux-toolkit
// import { Store } from 'redux' // for non-toolkit
import {
render as rtlRender,
RenderOptions,
RenderResult,
} from "@testing-library/react";
import { ReactElement, ReactNode } from "react";
import { Provider } from "react-redux";
import { configureStoreWithMiddlewares, RootState } from "../store";
type ReduxRenderOptions = {
preloadedState?: RootState;
store?: EnhancedStore; // for redux-toolkit
// store?: Store // for non-toolkit
renderOptions?: Omit<RenderOptions, "wrapper">;
};
function render(
ui: ReactElement,
{
preloadedState = {},
store = configureStoreWithMiddlewares(preloadedState),
...renderOptions
}: ReduxRenderOptions = {}
): RenderResult {
function Wrapper({ children }: { children?: ReactNode }): ReactElement {
return <Provider store={store}>{children}</Provider>;
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// re-export everything
export * from "@testing-library/react";
// override render method
export { render };
(this code is adapted from the Redux Testing Docs). Note that the Typescript is different for Redux-Toolkit than for plain Redux; use the lines that apply to your project (or no Typescript at all if that’s your jam).
The idea with the above code:
- The custom render in this doc takes a
preloadedStateand UI component. - The custom render wraps the UI component in a Redux Provider, with a store that contains the
preloadedState. - The code exports everything from @testing-library/react and then overrides the
rendermethod, so this file can be used in place of the actual @testing-library/react module (as we’ll see when we use it). - When importing from this file instead of @testing-library/react, all the methods except
render(such asscreenorfireEvent) will come straight from @testing-library/react — exceptrender, which has been replaced with our customrender.
Note that you can create a store beforehand and pass it to the render function, or you can use the default, which is creating a new store with your preloadedState, using all of the configuration from the configureStoreWithMiddlewares function that our production uses:
store = configureStoreWithMiddlewares(preloadedState),
If you do create a store and pass it as an argument, it’s very important that a new store is created for every test (so that there’s no sharing of state between tests).
Step 2: Using custom render in tests
To use this custom render in a test, we’ll import from our test-utils/index.tsx file instead of from @testing-library/react.
Say you have a user profile page, that looks like this:
The UserProfile component might look something like this:
import { EnhancedStore } from "@reduxjs/toolkit"; // for redux-toolkit
// import { Store } from 'redux' // for non-toolkit
import {
render as rtlRender,
RenderOptions,
RenderResult,
} from "@testing-library/react";
import { ReactElement, ReactNode } from "react";
import { Provider } from "react-redux";
import { configureStoreWithMiddlewares, RootState } from "../store";
type ReduxRenderOptions = {
preloadedState?: RootState;
store?: EnhancedStore; // for redux-toolkit
// store?: Store // for non-toolkit
renderOptions?: Omit<RenderOptions, "wrapper">;
};
function render(
ui: ReactElement,
{
preloadedState = {},
store = configureStoreWithMiddlewares(preloadedState),
...renderOptions
}: ReduxRenderOptions = {}
): RenderResult {
function Wrapper({ children }: { children?: ReactNode }): ReactElement {
return <Provider store={store}>{children}</Provider>;
}
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// re-export everything
export * from "@testing-library/react";
// override render method
export { render };
You can see that the user piece of state has name and email properties. To test that the user name and email show on the profile page, you need to preload the state with a user object for the test.
Here’s how the tests might look with our custom render method:
import { render, screen } from "../../test-utils"; // adjust for relative path to *your* test-utils directory
import { UserProfile } from "./UserProfile";
const fakeUser = {
name: "Tess Q. User",
email: "tess-user@fake.domain.com",
};
test("User profile shows name and email", () => {
render(<UserProfile />, { preloadedState: { user: fakeUser } });
expect(screen.getByText("Tess Q. User")).toBeInTheDocument();
expect(screen.getByText("tess-user@fake.domain.com")).toBeInTheDocument();
});
Here are the steps in the custom render method that make this work:
- The custom
rendermethod uses thepreloadedStateoption (and thecreateStoreWithMiddlewaresfunction used in production) to create a new store. - The custom
rendermethod then creates a wrapper with a Redux Provider, passing the store with the preloaded state as a prop. - The custom
rendermethod uses the actual testing-library/reactrenderto render theuiargument (in this case,<UserProfile />) wrapped in the newly-created Provider from step 2, and returns the result.
This result now has the store pre-populated with the specified user, the useSelector call in the component returns the fakeUser, and the tests pass.

Top comments (2)
Hey Bonnie do you have a repo for this, I'm a bit confused on how to organize the files
Hi, Miguel,
I'm so sorry for the delayed response!! You can see an example of the file organization in this project in my GitHub repo. Happy to answer any follow-up questions!
Cheers,
Bonnie