“How can Jest be used to test a redux toolkit slice that utilizes createAsyncThunk?”

I am looking to test the following simplified slice code snippet:

interface MyState {
  data: any[];
}

const initialState: MyState = {
  data: [],
};

export const postData = createAsyncThunk(
  'data/postData',
  async (
    param: {
      data: any[];
    },
    { rejectWithValue },
  ) => {
    try {
      const myService: MyService = new MyService();
      return await myService.postData(param.data);
    } catch (error) {
      console.error(error.message);
      return rejectWithValue(error.message);
    }
  },
);

const combineExtraReducers = (
  builder: ActionReducerMapBuilder<MyState>,
) => {
  builder.addCase(getData.fulfilled, (state, { payload }) => {
    state.data = payload;
  });
};

export const dataSlice = createSlice({
  name: 'data',
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    combineExtraReducers(builder);
  },
});

What is the best way to write a simple test for this slice? I am okay without using createAsyncThunk, but struggling to come up with a test involving the thunk.

The method suggested in the official redux toolkit documentation seems to be giving me timeout errors.

We rely on axios as our HTTP client library.

Answer №1

Steps:

  1. To begin, utilize

    jest.spyOn(MyService.prototype, 'postData')
    for mocking the postData method within the MyService class along with its resolved/rejected value.

  2. Next, establish a mock redux store to dispatch the async thunk and obtain the state using the store.getState() API for asserting any changes.

For example:

index.ts:

import { ActionReducerMapBuilder, createAsyncThunk, createSlice } from '@reduxjs/toolkit';
import { MyService } from './my-service';

interface MyState {
  data: any[];
}

const initialState: MyState = {
  data: [],
};

export const postData = createAsyncThunk(
  'data/postData',
  async (
    param: {
      data: any[];
    },
    { rejectWithValue },
  ) => {
    try {
      const myService: MyService = new MyService();
      return await myService.postData(param.data);
    } catch (error) {
      console.error(error.message);
      return rejectWithValue(error.message);
    }
  },
);

const combineExtraReducers = (builder: ActionReducerMapBuilder<MyState>) => {
  builder.addCase(postData.fulfilled, (state, { payload }) => {
    state.data = payload;
  });
};

export const dataSlice = createSlice({
  name: 'data',
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    combineExtraReducers(builder);
  },
});

my-service.ts:

export class MyService {
  async postData(params) {
    return ['real data'];
  }
}

index.test.ts:

import { configureStore } from '@reduxjs/toolkit';
import { dataSlice, postData } from './';
import { MyService } from './my-service';

describe('UniqueID', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  test('verifying successful posting of data', async () => {
    jest.spyOn(MyService.prototype, 'postData').mockResolvedValueOnce(['mock data']);
    const store = configureStore({ reducer: dataSlice.reducer });
    await store.dispatch(postData({ data: ['mock arg'] }));
    expect(store.getState()).toEqual({ data: ['mock data'] });
  });
});

Test outcome:

 PASS   redux-toolkit-example  packages/redux-toolkit-example/stackoverflow/UniqueID/index.test.ts
  UniqueID
    ✓ verifying successful posting of data (6 ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   85.71 |      100 |   83.33 |   83.33 |                   
 index.ts      |    87.5 |      100 |     100 |   85.71 | 24-25             
 my-service.ts |      80 |      100 |      50 |      75 | 3                 
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.609 s

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing the axios create method: troubleshooting and best practices

I am attempting to use the axios library in my Next.js app (written in TypeScript) to access a public API for retrieving IP addresses from . In my index.ts file, I have the following code: import axios from "axios"; export const ipApi = axios.cr ...

Exploring deeply nested arrays of objects until a specific condition is satisfied

My array is structured in a nested format as shown below. const tree = { "id": 1, "name": "mainOrgName", "children": [ { "id": 10, "name": "East Region", "children": [ ...

Utilizing the Vuex/Redux store pattern to efficiently share a centralized source of data between parent and child components, allowing for customizable variations of the data as

Understanding the advantages of utilizing a store pattern and establishing a single source of truth for data shared across components in an application is essential. Making API calls in a store action that can be called by components, rather than making se ...

I'd like some clarification on the code that dynamically adds routes using Typescript and Node Express. Can someone please

Running my API server with node/typescript and express / express validator, I came across this code that I found really useful for separating route logic: function createCustomRouter(route: Array<CustomRouteEntry>): Router { const customRouter = R ...

Accessing Child Properties in Parent Component using Typescript

New to the world of Typescript! Imagine having a component called TitleSubtitle that consists of both a Title and a Subtitle component. The Title component comes with props: interface TitleProps { text: string; } The Subtitle component also has props ...

Encountered an unexpected token error when executing karma-coverage in a project using TypeScript

I have been working on a simple Angular/Typescript project that includes 12 basic unit tests which all pass successfully. However, I am now looking to measure the code coverage of these tests. Despite trying different methods, I have not been able to achie ...

Using an existing function with no arguments as a handler in Typescript and React: A Step-by-Step Guide

NOTE: I'm still learning Typescript, so I may be missing something obvious here. Let's consider a basic scenario in React Javascript, using a Material-UI Button: // Closing dialog event handler without needing an 'event' argument const ...

Error in Typescript: Cannot find reference to @viewChild

I attempted to use the select() method in tabs.ts based on the Ionic Tabs documentation. However, upon running it, I encountered an error stating that "select is undefined". Upon further investigation, I realized that my viewChild was empty or undefined wh ...

Determine the difference between the sizes of two arrays

My current data structure looks like this: [ { name: "A", upvotes: [ "a", "b" ], downvotes: [ "a", "b", "c" ] }, { name: "B", ...

Accessing a variable within a function in Angular

Recently I started working with Angular and encountered an issue while trying to access a variable inside a function. Below is the code snippet that's causing me trouble: mergeImages() { var imgurl; var canvas: HTMLCanvasElement = this.canv ...

Using create-react-app with TypeScript for server-side rendering

My current project is built with create-react-app using typescript (tsx files). I'm now interested in implementing SSR for the project, but I'm not exactly sure where to begin. In the past, I've successfully implemented SSR with typescript ...

Develop a TypeScript class that includes only a single calculated attribute

Is it advisable to create a class solely for one computed property as a key in order to manage the JSON response? I am faced with an issue where I need to create a blog post. There are 3 variations to choose from: A) Blog Post EN B) Blog Post GER C) Bl ...

What is the benefit of utilizing ngSubmit over just using a basic button and function?

Lately, I've been pondering whether to utilize ngSubmit or simply bind a (click)="submit()" on a button. There's been much debate about using submit and ngSubmit, but is it necessary to rely on the traditional HTML submit method? Particularly wh ...

After the build/export process, the NextJS routing fails to function properly when I manually insert the URL

While testing my application on localhost using npm run dev, everything works normally. I can access routes like localhost:3000/settings/account and they render correctly. However, after running npm run build and npm run export, testing with serve -s out a ...

Template for event cell details in Angular2 calendar view

Currently utilizing [angular-calendar] from github.com/mattlewis92/angular-calendar . My goal is to incorporate my own template as a detailed view for events. I am aiming to achieve a similar effect as shown in the image: final effect So far, I ha ...

advanced reducer utilized in a react-redux application

I am currently part of a project built using React.js with redux and saga. The reducers in the store are implemented using a pipe function: export const HighestOrderReducer = (...higherOrderReducers) => (baseReducer) => higherOrderReducers.reduce( ...

Using TypeScript to Declare Third Party Modules in Quasar

I'm currently trying to integrate Dropzone-vue into my Quasar project. However, I've encountered an issue as I can't directly install and declare it in a main.js file due to the lack of one in Quasar's structure. Additionally, an error ...

The type 'FileUpload[][]' cannot be assigned to the type 'AngularFireList<FileUpload[]>'

I'm currently working on an Angular application integrated with Firebase for the purpose of uploading images to the database and retrieving them as well. upload-file.service.ts import {Injectable} from '@angular/core'; import {AngularFireD ...

Using Angular to import a JSON file stored locally

I am facing an issue with importing a .json file in my Angular project. The file is located outside the project structure as shown below: | common | configuration.json | angular-app | src | app | my-component | ...

The 'import.meta' meta-property can only be used with the '--module' set to 'es2020', 'esnext', or 'system'.ts(1343)

Whenever I attempt to utilize import.meta.url (as demonstrated in the Parcel docs), I am consistently met with the error message "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es ...