Reputation: 89
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
Reputation: 89
I used awaitFor() to make assertions, and data is available in the DOM.
Upvotes: 0