Set up React testing library with redux

November 9, 2022

Lady getting ready to write react tests
Photo by Sigmund on Unsplash

React testing library lets you create unit and integration tests for your react components. Unit tests ensure a single component will work reliably. Integration tests ensure your modules work together. You want your tests to emulate how a user will use your software as much as possible, because then you will know whether a user will encounter a bug. That's why it's important to use your state management library in your tests.

Many applictions use a state management library, such as redux, to manage the state of a react application. React components will need access to these stores that hold the application state. An example of state managed would be user preferences on an app such as theme colors, font choice, etc.

  1. Import the relevant components BrowserRouter, Provider, reducers, combineReducers...
  2. Create your store by combining your reducers for redux.
  3. Use the render function from '@testing-library/react' pass in a provider component with createStore(app_reducer) for the store prop. Put a BrowserRouter inside then inside that pass your component.
  4. ...props allows any object full of props to be passed to your component.

Check the example below

  Some code...
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { root_reducer } from './store/root_reducer';
import { createStore } from 'redux';
import preferences_reducer from './store/preferences_reducer';
import { combineReducers } from 'redux';

// props = null defaults the props to nothing if you don't want to pass props in to your component you are testing
export const RenderInProvider = (Component, props = null) => {
  const app_reducer = combineReducers({
    root_reducer,
    preferences_reducer,
  });

  render(
    <Provider store={createStore(app_reducer)}>
      <BrowserRouter>
        <Component {...props} />
      </BrowserRouter>
    </Provider>
  );
};

RenderInProvider will help keep your code DRY. Simply import it in one of your tests. In this contrived code example a Notifcation modal renders on the screen and the store would update a user has been notified after clicking 'OK', then it would disappear.

  Some code...
import { RenderInProvider } from '.RenderInProvider';
import Notification from './Notifcation';
import '@testing-library/jest-dom/extend-expect';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';


describe('Render a notification and it should go away after clicking okay', () => {
  it('should appear on the screen', async () => {
    // Wraps component tested in a provider with your store.
    // now it will have access to the store
    RenderInProvider(Notification);
    const okayText = await screen.findByText(/OK/gi);
    expect(okayText).toBeInTheDocument();
  });

  it('should go away after a user clicks the OK button', async () => {
    

    RenderInStore(Notification);
    const user = userEvent.setup();
    const okayText = await screen.findByText(/OK/gi);
     // after clicking OK the store's dispatch function would fire off
    await user.click(okayText);
    // the component would no longer appear in the document if the component only renders if 
    // it is conditionally rendered based on redux state.
    expect(okayText).not.toBeInTheDocument();
  });

});