Introduction
Often in our projects we need to manage the state. For this purpose, we can use Redux however in 2024 there is a Redux toolkit , which simplifies the use of Redux itself. In this tutorial, I want to focus on how to start testing an application that uses the Redux toolkit.
Custom render function.
In testing React components, It is often used external libraries, not only redux-toolkit, but also react-router-dom or styled-components . In these cases, it is usefull to create custom render function.
Let's take as example boardSlice.ts (example from my project):
Import the necessary modules and components from React, Redux Toolkit, React Testing Library, and your application.
import { ReactElement, ReactNode } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import { boardSlice } from './store/slices';
Define the CustomRenderOptions interface witch extends the RenderOptions
from @testing-library/react
, excluding the wrapper
property. It adds optional preloadedState
and store
properties. This allows you to pass an initial state for the Redux store or a custom store when calling customRender
.
interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
preloadedState?: Record<string, unknown>;
store?: ReturnType<typeof configureStore>;
}
Create the customRender function takes a React element and an options object as arguments. The options object defaults to an empty object if not provided.
Create the Redux store: If a store is not provided in the options, a new store is created using the configureStore
function from Redux Toolkit. The store uses the boardSlice.reducer
as its reducer and the preloadedState
as its initial state.
const customRender = (
ui: ReactElement,
{
preloadedState = {},
store = configureStore({
reducer: { board: boardSlice.reducer },
preloadedState,
}),
...options
}: CustomRenderOptions = {}
) => {
// ...
};
Create the Wrapper component, wraps its children in a Redux Provider
. The Provider
makes the Redux store available to the components.
const Wrapper = ({ children }: { children: ReactNode }) => (
<Provider store={store}>{children}</Provider>
);
Call the render function finally, the function calls the render
function from React Testing Library, passing the ui
and the Wrapper
as the wrapper
option.
return render(ui, { wrapper: Wrapper, ...options });
Export the customRender
function, then exported as render
, so it can be used in place of the render
function from @testing-library/react
.
export { customRender as render };
Finally we receive :
import { ReactElement, ReactNode } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import { boardSlice } from './store/slices';
interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
preloadedState?: Record<string, unknown>;
store?: ReturnType<typeof configureStore>;
}
const customRender = (
ui: ReactElement,
{
preloadedState = {},
store = configureStore({
reducer: { board: boardSlice.reducer },
preloadedState,
}),
...options
}: CustomRenderOptions = {}
) => {
const Wrapper = ({ children }: { children: ReactNode }) => (
// Here you can other providers
<Provider store={store}>
{children}
</Provider>
);
return render(ui, { wrapper: Wrapper, ...options });
};
export * from '@testing-library/react';
export { customRender as render };
Sumarry
In this tutorial, we've explored how to create a custom render function for testing React components that interact with a Redux store, using Redux Toolkit. This custom render function simplifies the testing process by automatically wrapping the component under test in the necessary context providers, such as Redux's Provider
and React Router's MemoryRouter
.
We started by importing the necessary modules and defining a CustomRenderOptions
interface to extend the RenderOptions
from @testing-library/react
. This interface allows us to optionally pass an initial state or a custom store to the customRender
function.
Next, we created the customRender
function, which takes a React element and an options object as arguments. Inside this function, we created a Redux store (if not provided in the options) and a Wrapper
component that wraps its children in a Redux Provider
.
Finally, we called the render
function from @testing-library/react
, passing the ui
and the Wrapper
as the wrapper
option, and exported the customRender
function as render
.
This custom render function is a powerful tool that can simplify your test setup, reduce repetition in your tests, and make your tests more robust and flexible when working with libraries like Redux Toolkit.
With this configuration, your application is now equipped to handle Redux Toolkit in unit tests. In subsequent sections, we will delve into the specifics of what to test and how to conduct these tests within the Redux Toolkit context.
Top comments (0)