Testing with store.dispatch(action) doesn't work with redux-toolkit

370 views Asked by At

Redux toolkit, redux-mock-store, redux-thunk

I'm struggling to understand the reason why my store.dispatch(action) doesn't work whereas userSlice.reducer seems to be working in the context of unit testing.

I did read this Weird error testing a reducer (redux toolkit) but it didn't help to resolve the issue.

user.tsx

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { refreshAccessToken } from "../../api/auth";
import { getUserData, patchUserData } from "../../api/user";
import { UserDataResponse } from "../../types/user";
import { RootState } from "../store";
import { logoutUserReducer } from "./auth";
export interface User {
  email?: string;
  name?: string;
  password?: string;
}

export interface InitialStateUser {
  user: User | null;
  userIsLoading: boolean;
  userIsLoaded?: boolean;
}

const initialState: InitialStateUser = {
  user: null,
  userIsLoading: false,
};

export const getUserDataReducer = createAsyncThunk(
  "getUserData",
  async (_, { rejectWithValue }) => {
    // some async code with FETCH API and getUserData()
    }
  }
);

export const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(getUserDataReducer.fulfilled, (state, action) => {
        state.user = action.payload.user;
        state.userIsLoading = false;
        state.userIsLoaded = true;
      })
      .addCase(getUserDataReducer.pending, (state) => {
        state.userIsLoading = true;
        state.userIsLoaded = false;
      })
      .addCase(getUserDataReducer.rejected, (state, action) => {
        state.userIsLoading = false;
        state.userIsLoaded = false;
      })
  },
});

export default userSlice.reducer;

user.test.tsx

import { AppDispatch, RootState } from "../store";
import configureMockStore, { MockStoreEnhanced } from "redux-mock-store";
import thunk from "redux-thunk";
import { initialIngredientsState } from "./ingredients.test";
import { initialOrderState } from "./order.test";
import userReducer, {
  getUserDataReducer,
  InitialStateUser,
  userSlice,
} from "../slices/user";
import fetchMock from "fetch-mock";
import { BASE_URL } from "../../api";
import { getCookie } from "../../utils/cookieHandler";

const initialUserState: InitialStateUser = {
  user: null,
  userIsLoading: false,
};

const mockStoreInitialState = {
  user: { ...initialUserState }
};

describe("user reducer", () => {
  let store: MockStoreEnhanced<RootState>;
  beforeEach(() => {
    store = mockStore(mockStoreInitialState);
  });
  afterEach(() => {
    fetchMock.restore();
  });
  // WORKS
  it("should getUserData fullfilled", () => {
    const newState = userSlice.reducer(initialUserState, {
      type: "getUserData/fulfilled",
      payload: {
        user: { email: "[email protected]", name: "testU2dfd" },
      },
    });

    expect(newState.user).toEqual({
      email: "[email protected]",
      name: "testU2dfd",
    });
    expect(newState.userIsLoaded).toBe(true);
  });
  // DOESN't WORK
  it("should getUserData pending", async () => {
    await store.dispatch(getUserDataReducer.pending(""));
    const { userIsLoaded, userIsLoading } = store.getState().user;
    console.log(store.getState());
    expect(userIsLoaded).toBe(false);
    expect(userIsLoading).toBe(true);
  });
});

The redux state is simply not updated for the await store.dispatch(getUserDataReducer.pending(""));.

● user reducer › should getUserData pending

    expect(received).toBe(expected) // Object.is equality

    Expected: false
    Received: undefined

Am I missing something here or misunderstood smth?

1

There are 1 answers

0
Ren On

Ok that looks to be that it's not even intended to update the state according to https://github.com/reduxjs/redux-mock-store/issues/71 and from readme https://github.com/reduxjs/redux-mock-store

Please note that this library is designed to test the action-related logic, not the reducer-related one