Ram Kumar
Ram Kumar

Reputation: 89

Redux testing- asyncthunk not updating redux state

I am learning React and Redux. I have been trying to do integration testing on a react component, that dispatches an action to fetch data from external API. I have used MSW to mock the fetch request. I have also used a Reusable Test Render Function as in redux documentation. when I custom render the component, I expect the mock data from API to be updated in the state and rendered in another component. So far, I see that the store is not getting updated with data from API. I have checked the MSW api mock. It is returning the value I have provided. Can someone please point me in the right direction.

my test file

import { screen } from '@testing-library/react';
import renderWithProvider from '../utils/test-util';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import MissionsPage from '../components/missions/MissionsPage';

const missions = [
  {
    id: 'testmission1',
    name: 'test1',
    descriptions: 'test mission',
    isReserved: false,
    wiki: '',
  },
];

export const handlers = [
  rest.get('https://api.spacexdata.com/v3/missions', (req, res, ctx) => {
    return res(ctx.json(missions));
  }),
];

const server = setupServer(...handlers);

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test('testing Mission page UI', async () => {
  const { store } = renderWithProvider(<MissionsPage />);
  console.log(store.getState().missions);
  const text = screen.getByText('/test mission/i');
  expect(await text.toBeInTheDocument());
});

Component I am trying to render

import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';
import MissionsTable from './MissionsTable';
import { fetchMissions } from '../../redux/missions/missionSlice';

const MissionsPage = () => {
  const dispatch = useDispatch();
  const status = useSelector((state) => state.missions.toFetch);
  useEffect(() => {
    if (status) {
      dispatch(fetchMissions());
    }
  }, [status, dispatch]);
  return (
    <MissionsTable />
  );
};

export default MissionsPage;

my reusable test render function

import React from 'react';
import { render } from '@testing-library/react';
import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import missionSliceReducer from '../redux/missions/missionSlice';

function renderWithProviders(
  ui,
  {
    preloadedState = {},
    store = configureStore({
      reducer: { missions: missionSliceReducer },
      preloadedState,
    }),
    ...renderOptions
  } = {}
) {
  function Wrapper({ children }) {
    return <Provider store={store}>{children}</Provider>;
  }

  // Return an object with the store and all of RTL's query functions
  return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}

export default renderWithProviders;

slice where I have defined the Async thunk.

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

const initialState = { missions: [], toFetch: 'true' };

export const fetchMissions = createAsyncThunk('missions/fetchMissions', async () => {
  const response = await fetch('https://api.spacexdata.com/v3/missions');
  return response.json();
});

const missionSlice = createSlice({
  name: 'missions',
  initialState,
  reducers: {
    bookMission: (state, { payload }) => {
      const newArr = state.missions.map((misObj) => {
        if (misObj.id === payload) {
          if (misObj.isReserved === true) {
            return { ...misObj, isReserved: false };
          }
          return { ...misObj, isReserved: true };
        }
        return ({ ...misObj });
      });
      const newState = { ...state };
      return { ...newState, missions: newArr };
    },
  },
  extraReducers(builder) {
    builder.addCase(fetchMissions.fulfilled, (state, action) => {
      const newState = { ...state };
      const missionList = [];
      action.payload.forEach((misObj) => {
        missionList.push({
          id: misObj.mission_id,
          name: misObj.mission_name,
          description: misObj.description,
          wiki: misObj.wikipedia,
          isReserved: false,
        });
      });
      newState.missions = missionList;
      newState.toFetch = false;
      return newState;
    });
  },
});

export const { bookMission } = missionSlice.actions;
export default missionSlice.reducer;

this is the error I am getting.

 console.log
    { missions: [], toFetch: 'true' }

      at Object.<anonymous> (src/__tests__/missionUI.test.js:31:11)

 FAIL  src/__tests__/missionUI.test.js
  ✕ testing Mission page UI (85 ms)

  ● testing Mission page UI

    TestingLibraryElementError: Unable to find an element with the text: /test mission/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    Ignored nodes: comments, script, style
    <body>
      <div>
        <div>
          <div
            class="table-responsive-sm"
          >
            <table
              class="missionTable table table-striped table-bordered table-hover"
            >
              <thead>
                <tr>
                  <th>
                    Mission
                  </th>
                  <th>
                    Description
                  </th>
                  <th>
                    Status
                  </th>
                  <th>
                     
                  </th>
                </tr>
              </thead>
              <tbody />
            </table>
          </div>
        </div>
      </div>
    </body>

      30 |   const { store } = renderWithProvider(<MissionsPage />);
      31 |   console.log(store.getState().missions);
    > 32 |   const text = screen.getByText('/test mission/i');
         |                       ^
      33 |   expect(await text.toBeInTheDocument());
      34 | });
      35 |

Upvotes: 1

Views: 507

Answers (1)

Ram Kumar
Ram Kumar

Reputation: 89

I used awaitFor() to make assertions, and data is available in the DOM.

Upvotes: 0

Related Questions