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 getState, dispatch, 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 getState, dispatch, 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
-
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.
-
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.
-
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.
-
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!