Table of contents
1.
Introduction
2.
Setup
3.
Testing Actions and Reducers
3.1.
Program:
3.2.
Program:
4.
Testing Middleware
4.1.
Program:
4.2.
Output:
5.
FAQs
6.
Key Takeaways
Last Updated: Mar 27, 2024

Testing Redux with Jest

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Redux is a handy state management library for Javascript Applications. It is a predictable state container that has simplified state management for Javascript applications.

If you are unfamiliar with Redux or working with Redux, you can learn more here.

We can use any available test runners to test Redux, but we will use the Jest framework in this blog. Jest is a prevalent open-source testing framework built by Facebook on JavaScript. If you are unfamiliar with testing with Jest, you can check out our Getting Started with Jest article.

In this article, we will explore the setup and testing implementation of Redux using Jest with the help of some examples.

Setup

To start testing with Jest, You will first need to create a new Node Project and make sure that you have initialized a package.json file in your projects directory. You can do this by following the Jest projects set up here.

Once you have your project ready, you can install the dependencies like this:

npm install redux redux-saga

That will install the redux and redux-saga dependencies to your package.json. If you have not installed Jest yet that you can do that like this:

npm install --save-dev jest

And to use it together with babel, you will need to install babel-jest like this:

npm install --save-dev babel-jest

Now, you need to configure .babelrc file by adding to use babel-present-env:

{ 
   presets: ["@babel/preset-env"] 
}

And configure package.json by adding the following custom scripts to it’s top level:

"scripts": {
     "test": "jest",
     "test:watch": "npm test -- --watch"
  },

Testing Actions and Reducers

In simple words, Actions and Reducers provide the ability to maintain a global state container in Redux. 

When an application's state changes, that part would dispatch an Action (similar to an object) directly related to or indicates what happened.

A Reducer gets hold of this action and changes the state tree that will modify the state. After doing this, the global store alerts the part of the application that initiated the change.

It is essential to test Action creators and Reducers because they play a significant role. You do not need to explicitly test the return value of action creators, as they are considered an implementation detail within your application.

Testing Reducers is straightforward because they are pure functions. To test Reducers, you can call them with a specific input state and action and assert that the resulting state matches expectations.

The following example tests reducers and also use actions implementation in it. We wrote this example in typescript. Using TypeScript to Redux Applications is the recommended approach due to many key benefits such as catching errors earlier in development, enabling safer and more efficient refactoring, and acting as documentation for existing source code. You can learn more about the difference between typescript and javascript here.

Program:

// reduxtest.ts

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

const initialState = [
  {
     text: "Use Redux",
     completed: false,
     id: 0,
  },
];

const todosSlice = createSlice({
  name: "todos",
  initialState,
  reducers: {
     todoAdded(state, action: PayloadAction<string>) {
        state.push({
           id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
           completed: false,
           text: action.payload,
        });
     },
  },
});

export const { todoAdded } = todosSlice.actions;

export default todosSlice.reducer;
You can also try this code with Online Javascript Compiler
Run Code

Program:

// reduxtest.test.ts
import reducer, { todoAdded } from "./reduxtest";

test("should handle a todo being added to an empty list", () => {
  const previousState = [];
  expect(reducer(previousState, todoAdded("Run the tests"))).toEqual([
     {
        text: "Run the tests",
        completed: false,
        id: 0,
     },
  ]);
});

test("should handle a todo being added to an existing list", () => {
  const previousState = [
     {
        text: "Run the tests",
        completed: true,
        id: 0,
     },
  ];
  expect(reducer(previousState, todoAdded("Use Redux"))).toEqual([
     {
        text: "Run the tests",
        completed: true,
        id: 0,
     },
     {
        text: "Use Redux",
        completed: false,
        id: 1,
     },
  ]);
});
You can also try this code with Online Javascript Compiler
Run Code

Output:

Testing Middleware

Testing asynchronous actions are tricky, which is why middleware like redux-saga and redux-thunk is very handy, especially when it comes to testability.

In Redux, Middleware functions wrap the behavior of dispatch calls. To test this modified behavior, we need to mock the behavior of the dispatch call.

We will create a manual middleware function similar to the real redux-thunk for our example. Redux Thunk is a middleware that allows you to return functions within Redux instead of just actions. That provides for delayed actions, including working with promises.

We will start off by creating our Middleware function.

Program:

const thunk =
  ({ dispatch, getState }) =>
  (next) =>
  (action) => {
     if (typeof action === "function") {
        return action(dispatch, getState);
     }

     return next(action);
  };
You can also try this code with Online Javascript Compiler
Run Code

We will use jest.fn() to create mocks for getStatedispatch, and next functions. That is known as mocking. You can learn more about Manual Mocks here.

Program:

const create = () => {
  const store = {
     getState: jest.fn(() => ({})),
     dispatch: jest.fn(),
  };
  const next = jest.fn();

  const invoke = (action) => thunk(store)(next)(action);

  return { store, next, invoke };
};
You can also try this code with Online Javascript Compiler
Run Code

We can now test the functionality of our middleware by checking whether it calls getStatedispatch, and subsequent functions at the right time. And the combination of all three of these codes will constitute our TypeScript file(reduxtest.test.ts).

Program:

test("passes through non-function action", () => {
  const { next, invoke } = create();
  const action = { type: "TEST" };
  invoke(action);
  expect(next).toHaveBeenCalledWith(action);
});

test("calls the function", () => {
  const { invoke } = create();
  const fn = jest.fn();
  invoke(fn);
  expect(fn).toHaveBeenCalled();
});

test("passes dispatch and getState", () => {
  const { store, invoke } = create();
  invoke((dispatch, getState) => {
     dispatch("TEST DISPATCH");
     getState();
  });
  expect(store.dispatch).toHaveBeenCalledWith("TEST DISPATCH");
  expect(store.getState).toHaveBeenCalled();
});
You can also try this code with Online Javascript Compiler
Run Code

Output:

FAQs

  1. What is a state in redux?
    A state in Redux is a JavaScript object. We store the application's internal state as its properties in a state in Redux, For example, which user is logged on. After logging in, you need to remember who that user is even when the user navigates on different pages. That piece of information is a part of your application's internal state.
     
  2. What is integration testing?
    Integration Testing testing technique where software modules are integrated logically and tested. The primary purpose of this level of testing is to expose defects in the interaction between software modules when combined.
     
  3. What is a unit test in React?
    A Unit test is a testing method that tests an individual software unit in isolation. That involves verifying the output of a function or component for a given input. In the case of React components, this means checking that the component renders correctly for the specified props.
     
  4. What is the use of Redux in React JS?
    Redux allows React components to read data from a Redux Store and dispatch Actions to the Store to update data. It also helps apps scale by providing a sensible way to manage the state through a unidirectional data flow model. React Redux is conceptually straightforward and the official React binding for Redux.

Key Takeaways

Testing React applications is straightforward using Jest. We have worked with modules and packages that allow easy implementation of testing Redux through this article. Actions and Creators are particularly testable components in Redux, and We also worked with a Redux Middleware example similar to Redux-Thunk.

We have barely scratched the surface of what these packages and Redux have to offer. There is a lot more you can do with them. So be sure to check the Modules article on Coding Ninjas and head over to our practice platform Coding Ninjas Studio to practice top problems, attempt mock tests, read interview experiences, and much more. Till then, Happy Learning!

Live masterclass