Introduction To Redux Toolkit Testing 

Photo of author

Testing is a fundamental aspect of software development, and Redux provides a structured, convenient way to test your redux rationale. Redux Toolkit has a set of utilities and tools that make it easier to test your redux code.

In brief, the introduction to Redux Toolkit testing covers areas such as installing dependencies, understanding the need for writing tests for redux logic, testing components connected to the Redux store, testing selectors, and running tests.

This article will cover some coding examples to enlighten concepts and solidify your understanding of the subject matter. With this introduction to Redux Toolkit testing, you’ll emerge well-equipped for a test session with more substantial confidence destined for success.

Table of Contents

Understanding The Basics: Prerequisites and Setup

Testing using Redux Toolkit involves setting up and testing libraries and tools that ensure the credibility and reliability of your generated Redux code. Before running onto testing grounds, it is essential to have all required tools installed and readily set up.

This section will introduce the essential prerequisites and setup for Redux Toolkit testing.

Prerequisites

Nodes.js and NPM –

To test in the future, it is mandatory to have nodes.js and Node Package Manager (npm) adequately installed on your PC. For installation, the two can be downloaded from https://nodejs.org/.

Nodes.js and npm

Create React App (optional)

If you’re using React, a React application, it would be wise to use Create React App for your project setup. The following command can help you set up globally:

 (bash)

                     npx create-react-app my-redux-app

            cd my-redux-app

Redux Installation

Install Redux Toolkit –

After completing the previous steps, install the toolkit with functions and utilities for co-functioning with Redux. Install the ‘@reduxjs/toolkit’ package. In the environment, the command looks like this,

(bash)

npm install @reduxjs/toolkit react-redux

Testing Dependencies

Install Jest –

Specific frameworks work best with Redux Toolkit, and Jest falls under the excellent JavaScript testing structures category.

Install Jest

(bash)

npm install – -save-dev jest

Install React Testing Library –

It’s intuitive to work with the React Testing Library with React components. If you are working with React, the following code will be helpful.

(bash)

npm install – – save-dev @testing-library/react @testing-library/jest-dom

Install Redux Testing Library –

To enjoy the additional utilities that come with the Redox Testing Library, install the testing library,

(bash)

npm install – -save-dev @reduxjs/toolkit/query/react/

Configure Jest

Ensure that you create a ‘jest.config.js’ in the root directory of your project. Alternatively, you can update your ‘package.json,’ which ensures the inclusion of your jest configuration

(json)

// jest.config.js

module.exports = {

// Add configuration options here

};

Redox Set up a Store For Testing

Make a Redux store –

To set up your Redux store with critical middleware and reducers, you can make use of the Redux Toolkit ‘configure store’:

Make a Redux store -

(Javascript)

// src/app/store.js

import { configureStore } from ‘@reduxjs/toolkit’;

import rootReducer from ‘./reducers’;

const store = configureStore({

reducer: rootReducer,

// Add middleware if needed

});

export default store;

Make the store available in your app –

End your application with the ‘Provider’ component from ‘react-redux’ as shown below. This will make your previously created Redux store available to your components:

(Javascript)

// src/index.js

import React from ‘react’;

import ReactDOM from ‘react-dom’;

import { Provider } from ‘react-redux’;

import store from ‘./app/store’;

import App from ‘./App’;

ReactDOM.render(

<Provider store={store}>

<App />

</Provider>,

document.getElementById(‘root’)

);

See Also: React Query with Redux: Everything to Know

Unit Testing In Redux Toolkit

In this section of our introduction to Redux Toolkit testing, we’ll thoroughly brush through unit testing. Unit testing involves individual units of code being tested in isolation from the rest of the application.

Unit Testing In Redux Toolkit

These unit codes include reducers, selectors, and action creators. To understand them better, we’ll plunge into several examples illuminating how they function on different parts of the Redux Toolkit.

Testing/Trying Reducers –

Redux functions are particular types of functions in that they are pure functions. They take the current state and an action, ultimately returning a new form. Here’s how you can use Jest to test reducers.

(javascript)

// counterSlice.js

import { createSlice } from ‘@reduxjs/toolkit’;

const counterSlice = createSlice({

name: ‘counter’,

initialState: { value: 0 },

reducers: {

increment: (state) => {

state.value += 1;

},

decrement: (state) => {

state.value -= 1;

},

},

});

export const { increment, decrement } = counterSlice.actions;

export default counterSlice.reducer;

(javascript)

// counterSlice.test.js

import counterReducer, { increment, decrement } from ‘./counterSlice’;

test(‘increment action increments the counter’, () => {

const initialState = { value: 1 };

const newState = counterReducer(initialState, increment());

expect(newState.value).toEqual(2);

});

test(‘decrement action decrements the counter’, () => {

const initialState = { value: 1 };

const newState = counterReducer(initialState, decrement());

expect(newState.value).toEqual(0);

});

Trying/Testing Action Creators

On the other hand, action creators are those functions that return an action object. Testing them will ensure their reliability and correctness on return on actions.

(javascript)

// counterSlice.js

import { createSlice } from ‘@reduxjs/toolkit’;

const counterSlice = createSlice({

name: ‘counter’,

initialState: { value: 0 },

reducers: {

increment: (state) => {

state.value += 1;

},

},

});

export const { increment } = counterSlice.actions;

export default counterSlice.reducer;

(javascript)

// counterSlice.test.js

import { increment } from ‘./counterSlice’;

 

test(‘increment action creator creates the correct action’, () => {

const action = increment();

expect(action.type).toEqual(‘counter/increment’);

});

Testing Selectors

Selector functions are those functions that retrieve data from the Redux store. Here’s how you can test them out to ensure their credibility:

(Javascript)

// counterSlice.js

import { createSlice } from ‘@reduxjs/toolkit’;

const counterSlice = createSlice({

name: ‘counter’,

initialState: { value: 0 },

reducers: {

// …reducers

},

});

export const selectCounterValue = (state) => state.counter.value;

export default counterSlice.reducer;

(javascript)

// counterSlice.test.js

import { selectCounterValue } from ‘./counterSlice’;

test(‘selectCounterValue selector returns the correct value’, () => {

const state = { counter: { value: 42 } };

const result = selectCounterValue(state);

expect(result).toEqual(42);

});

Async Logic Testing (Thunks) –

Peradventure, you have async logic using thunks. You can make use of ‘redux-mock-store’ to test them out:

(Javascript)

// asyncSlice.js

import { createSlice, createAsyncThunk } from ‘@reduxjs/toolkit’;

export const fetchData = createAsyncThunk(‘async/fetchData’, async () => {

const response = await fetch(‘https://api.example.com/data’);

const data = await response.json();

return data;

});

const asyncSlice = createSlice({

name: ‘async’,

initialState: { data: null, status: ‘idle’ },

reducers: {},

extraReducers: (builder) => {

builder.addCase(fetchData.pending, (state) => {

state.status = ‘loading’;

});

builder.addCase(fetchData.fulfilled, (state, action) => {

state.status = ‘succeeded’;

state.data = action.payload;

});

},

});

 

export default asyncSlice.reducer;

 

(javascript)

// asyncSlice.test.js

import configureStore from ‘redux-mock-store’;

import thunk from ‘redux-thunk’;

import { fetchData } from ‘./asyncSlice’;

const middlewares = [thunk];

const mockStore = configureStore(middlewares);

test(‘fetchData thunk dispatches the correct actions’, async () => {

const store = mockStore({});

await store.dispatch(fetchData());

const actions = store.getActions();

expect(actions[0].type).toEqual(‘async/fetchData/pending’);

expect(actions[1].type).toEqual(‘async/fetchData/fulfilled’);

});

The examples we covered are basic, friendly to beginners, and for those who need the logic in digested form. Therefore, this means that a few configurations, adjustments, and libraries will need to be added to get the desired outputs.

Integration Testing: A Comprehensive Approach

Unlike unit testing, integral testing puts relationships and interactions between various parts of your application on trial. The primary focus is seeing how the Redux state management melds with your components and the logic.

Integration Testing

In this section of our introduction to Redux Toolkit testing, we’ll venture into a comprehensive approach to Redux testing.

Setup

Testing library installation –

To enable testing, confirm that all testing libraries are correctly installed. Some of the Redux application testing libraries you can make use of include ‘@testing-library/user-event’, ‘@testing-library/react’, and ‘@testing-library/jest’:

(bash)

npm install –save-dev @testing-library/react @testing-library/jest-dom @

Configure test –

For testing react applications, there are some preset configurations. Ensure that your Jest and these configs are properly set up.

(json)

// jest.config.js

module.exports = {

// … other configurations

preset: ‘react’,

setupFilesAfterEnv: [‘@testing-library/react/cleanup-after-each’],

};

Redux Toolkit setup

Construct Redux Store –

With the help of Redux Toolkit, set up your Redux store. In the process, ensure that your application uses Redux Provider. This will make your components enclosed with the store.

Integration Test writing

Component-Redux interaction –

Test how your components and Redux store are doing together at this stage. Ultimately, within a Redux Provider, furnish or render your component, then use Redux actions to reproduce user interactions. Eventually, check if the environment is correctly set up.

(jsx)

// Example Test

import React from ‘react’;

import { render, screen } from ‘@testing-library/react’;

import { Provider } from ‘react-redux’;

import store from ‘./path/to/your/store’;

import MyComponent from ‘./path/to/your/component’;

test(‘Component updates Redux state correctly’, () => {

render(

<Provider store={store}>

<MyComponent />

</Provider>

);

// Perform user actions on the component (e.g., click a button)

// Check if the Redux state is updated as expected

// Use screen queries to assert the presence of certain elements based on state

});

Async operations –

In the case that your application has asynchronous operations, put Redux to the test and see how it manages these operations.

(jsx)

// Example Test

import { render, screen, waitFor } from ‘@testing-library/react’;

import { Provider } from ‘react-redux’;

import store from ‘./path/to/your/store’;

import MyAsyncComponent from ‘./path/to/your/async-component’;

test(‘Async component works correctly’, async () => {

render(

<Provider store={store}>

<MyAsyncComponent />

</Provider>

);

// Wait for the async operation to complete

await waitFor(() => {

// Assert that the component displays the expected content after async operation

expect(screen.getByText(‘Expected Text’)).toBeInTheDocument();

});

});

Full application integration –

If you’re working on a complete application, tests involving multiple components, reducers, and actions are the best. This route guarantees a seamless integration process throughout.

Mocking Dependencies

If any external dependencies are interacting with your components like APIs, make use of mocking libraries. These will isolate your Redux-related code to enable isolated testing.

Monitoring and Debugging

Exploit debugging tools and middleware such as Redux DevTools for state inspection. When identifying test failures.

While coding and testing, there are scenarios where unintended changes in your Toolkit application’s state ensue—snapshot testing steps in as the hero of the day through its valuable early detection and warning system.

Monitoring and Debugging

It captures a snapshot of the rendered output and juxtaposes the outcome with the previously saved image. This ingenious technique helps fish out sudden changes, alerting any forms of regression or bugs. Here are some steps and procedures you can follow to implement snapshot testing:

Setup

Install Jest –

Install jest if not installed already:

(bash)

npm install –save-dev jest

Configure Jest –

Set up a jest configuration file to enable specified snapshot testing settings. In the example below, the serializer used is ‘@emotion/jest/enzyme-serializer’. It is advised to use a serializer according to the type of project you’re working on:

(Javascript)

// jest.config.js

module.exports = {

// … other configurations

snapshotSerializers: [‘@emotion/jest/enzyme-serializer’],

};

Redux Toolkit setup

The Redux Toolkit is an essential component. Make sure that it is appropriately set up and ready for use. Set up may involve equipping the store with appropriate middleware like Thunk, which handles async actions.

Snapshot Tests Writing

Component Snapshots –

Prepare snapshot tests for your components in React. Render those components. Use Jes’s snapshot testing function to compare and capture components.

(jsx)

// Example Test

import React from ‘react’;

import { render } from ‘@testing-library/react’;

import MyComponent from ‘./path/to/your/component’;

test(‘MyComponent snapshot’, () => {

const { asFragment } = render(<MyComponent />);

expect(asFragment()).toMatchSnapshot();

});

Redux state components –

You might wonder if taking snapshots of the Redux state is possible. That’s doable by taking pictures of the serialized states.

(Jsx)

// Example Test

import store from ‘./path/to/your/store’;

test(‘Redux state snapshot’, () => {

const state = store.getState();

expect(state).toMatchSnapshot();

});

 Snapshots Update

Jest has a command option that allows you to intentionally change your components.

(bash)

npx jest –updateSnapshot

Continuous Integration

Lastly, meld your prepared snapshot testing to your CD/CI to avoid changes to your code that unintentionally alter the snapshots.

Automating and Running Tests

Automation and running tests aid in streamlining the testing process. The Redux Toolkit project sets up tools, scripts, and configurations to achieve this.

Automating and Running Tests

Project Structure

It is essential to have a well-structured project. Arrange tests based on the feature they are testing. Usually, test files are placed in a file like ‘tests’ or ‘_ _tests_ _’

(Plaintext)

project-root

|– src

| |– components

| |– redux

|– tests

| |– component-tests

| |– redux-tests

Configure Test

Jest should be framed or configured correctly to ensure a smooth operation. All necessary configuration commands should be housed in the file ‘jest.config.js’:

(javascript)

// jest.config.js

module.exports = {

// … other configurations

testEnvironment: ‘jsdom’,

moduleNameMapper: {

// … module mappings if needed

},

setupFilesAfterEnv: [‘<rootDir>/setupTests.js’],

snapshotSerializers: [‘enzyme-to-json/serializer’],

};

Create Setup Files

Build a setup file to place any crucial configurations meant for testing libraries such as Enzyme:

 

(Javascript)

// setupTests.js

import Enzyme from ‘enzyme’;

import Adapter from ‘enzyme-adapter-react-16’;

import { createSerializer } from ‘enzyme-to-json’;

Enzyme.configure({ adapter: new Adapter() });

expect.addSnapshotSerializer(createSerializer({ mode: ‘deep’ }));

Test Scripts In ‘package.json’

To include commands for running tests, update the ‘package.json’ section of your package:

(json)

// package.json

{

“scripts”: {

“test”: “jest”,

“test:watch”: “jest –watch”,

“test:coverage”: “jest –coverage”

// … other scripts

}

}

Run Tests Locally

Locally execute the tests using the configured scripts:

(bash)

npm test

Continuous Integration

A common practice is to integrate or meld your test procedures into your CD/CI, which aids in test automation whenever intentional or unintentional changes to your codebase are made:

(yaml)

# .github/workflows/test.yml

name: Run Tests

on:

push:

branches:

– main

jobs:

test:

runs-on: ubuntu-latest

steps:

– name: Checkout code

uses: actions/checkout@v2

– name: Set up Node.js

uses: actions/setup-node@v2

with:

node-version: ’14’

– name: Install dependencies

run: npm install

– name: Run tests

run: npm test

Monitor and Review

Lastly, establish a consistent review routine of your CI/CD environment. Investigations will enable you to pinpoint any due fixtures and promptly address them.

Achieving Optimal Test Coverage

To attain optimal test coverage, Redux selectors, actions, and reducers, which are different components of the Redux Toolkit project application, are tried.

Achieving Optimal Test Coverage

In this portion of our introduction to Redux Toolkit testing, we’ll cover steps that can be followed to attain maximum coverage.

Understand Your Codebase

First and foremost, understand your codebase. Get to know your Redux store structure, components, the relationships between elements, and your data flow patterns.

Pinpoint Critical Paths –

To obtain the most test coverage, identify important functionalities and crucial paths. Focus more on testing the most used sections of your codebase and make sure these sections are reliable and resilient.

Exploit Test Coverage Tools –

Take advantage of test coverage tools like those found in Jest. Such tools will guide you through sections that need further testing and areas that have been thoroughly tried.

(bash)

npm test — –coverage

Test Redux Actions –

Thoroughly test your Redux actions and your application’s associated action creators. Ensure that tests are dispatched correctly and that expected state changes are what occur.

Test Selectors –

Do not skip testing selectors if they are used to extract computed values from the Redux state. Observe if they return expected results according to the state conditions.

Test Reducers –

Examine reducers and ensure they manage actions properly. Examine all action types and default cases and observe if expected state changes occur.

Test Asynchronous Operations –

If your application is using async operations, test them out. As mentioned in previous sections, leverage tools like ‘redux-mock-store’ to simulate asynchronous behavior and mimic the Redux store.

Test Connected Components –

Test all React components linked to the Redux store. Ensure they forward actions correctly and render according to the Redux state. Resources like React Testing Library and Enzyme are there for component testing.

Error Handling and Edge Cases –

Bring all error-handling and edge cases to the test. Observe how your application behaves when taken by surprise. Check if error states are handled with poise.

Mock External Dependencies –

An aspect of your Redux application is external dependencies like APIs. Mock these when testing Redux components. This step helps your tests focus appropriately on your code instead of external services.

Review and Recode Tests –

Regularly review your tests and rewrite them accordingly. As your code evolves, ensure you also grow to avoid redundant tests.

Review and Recode Tests

Automate Test Execution –

As mentioned before, integrate the tests into your CI/CD so that tests are automatically triggered when changes in the codebase arise.

Testing Redux Toolkit With Typescript

Here is a comprehensive step-by-step guide for testing the Redux Toolkit with typescript.

Jest Setup With Typescript

Install all required packers that enable Jest and Typescript to function well.

(bash)

npm install –save-dev jest ts-jest @types/jest

Make a ‘jest.config.js’ file afterwards:

(javascript)

// jest.config.js

module.exports = {

preset: ‘ts-jest’,

testEnvironment: ‘jsdom’,

moduleNameMapper: {

// add any module mappings if needed

},

setupFilesAfterEnv: [‘<rootDir>/src/setupTests.ts’],

};

Typescript Configuring

See to it that your ‘tsconfig.json’ is correctly set for testing. Make sure you include configurations like ‘“esModulInterop”: true’, and ‘“jsx”: “react”’.

Typescript Configuring

Testing Actions

Just like in other methods, write tests for your Redux actions:

(typescript)

// actions.test.ts

import { increment, decrement } from ‘./actions’;

test(‘increment action’, () => {

const action = increment();

expect(action.type).toEqual(‘INCREMENT’);

});

test(‘decrement action’, () => {

const action = decrement();

expect(action.type).toEqual(‘DECREMENT’);

});

See Also: Mastering Redux-Saga Typescript: Full Guide

Testing Reducers

Write and set tests for your reducers. You should expect typescript to expose problems with action payloads and state shape:

(typescript)

// reducers.test.ts

import counterReducer from ‘./reducers’;

import { increment, decrement } from ‘./actions’;

test(‘counterReducer increments’, () => {

const initialState = { count: 0 };

const nextState = counterReducer(initialState, increment());

expect(nextState.count).toEqual(1);

});

test(‘counterReducer decrements’, () => {

const initialState = { count: 1 };

const nextState = counterReducer(initialState, decrement());

expect(nextState.count).toEqual(0);

});

See Also: JavaScript vs ReactJS: A Comparative Analysis

Testing Selectors

Also, write tests for your Redux selectors. Typescript will make sure that selected values have the appropriate types:

(typescript)

// selectors.test.ts

import { selectCount } from ‘./selectors’;

test(‘selectCount selector’, () => {

const state = { counter: { count: 42 } };

const result = selectCount(state);

expect(result).toEqual(42);

});

See Also: 6 Best Web Development Books: Top Recommendations for 2024

Testing Connected Components

In the case of React components linked with the Redux store, write tests to ensure their proper functionality:

(Typescript)

// MyComponent.test.tsx

import React from ‘react’;

import { Provider } from ‘react-redux’;

import { render, screen } from ‘@testing-library/react’;

import configureStore from ‘redux-mock-store’;

import MyComponent from ‘./MyComponent’;

const mockStore = configureStore();

test(‘MyComponent renders with Redux state’, () => {

const initialState = { counter: { count: 5 } };

const store = mockStore(initialState);

render(

<Provider store={store}>

<MyComponent />

</Provider>

);

expect(screen.getByText(‘Count: 5’)).toBeInTheDocument();

});

Test Runs

After all has been set, run your tests using the command below.

(bash)

npm test

See Also: Working With Data: How To Import JSON Files In React

FAQs

Why use the Redux Toolkit?

Several reasons are pinned to why Redux Toolkit merits, and these include boilerplate reduction, a simplified Redux setup procedure, immutability helpers, reducers and actions all in one, async logic simplification, backward compatibility, built-in immutability, etc.

What is Redux used for?

Redux is a designated state management library that is typically used with React. However, it is not limited to react alone but can be used with other libraries and Javascript frameworks. Its creation was and is to manage the state of an application in a predictable, transparent, and centralized manner.

Is Redux easy to test?

Absolutely! Redux was made to be simple to test as its frameworks are straightforward. Writing unit codes for various parts of your application is inappropriate, including middleware, selectors, reducers, and action creators.

What is the weakness of Redux?

Redux is a popular state management library commonly praised and commended for its clarity and straightforwardness; however, there are some setbacks to its use, though not inherent. Some of these include boilerplate code issues, a considerable learning curve, and integration issues with specific libraries, to name a few.

Conclusion

As we wrap our introduction to the Redux Toolkit testing session, these measures aim to streamline test processes, minimize error encounters, and establish robust mechanisms in your Redux application.

Whether for a complex or simple task, the steps outlined in this article, backed with detailed codes and commands, will surely win you a successful journey with the Redux toolkit.

Explore the performance between Redux Thunk and Redux Saga.

See Also: How to Build React State Management Without Redux?

Leave a Comment