0

I am trying to test a react app where it is fetching the data from jsonplaceholder. The fetch function is implemented in redux thunk via create async thunk.

i followed every guide and every related answer on stackoverflow regarding this but got no working answer.

i'm using msw for mock api fetching.


import {fireEvent,screen,render, findByText, waitFor, waitForElementToBeRemoved} from '@testing-library/react'
import { createMemoryHistory } from 'history'
import { BrowserRouter, Router } from 'react-router-dom'
import Dashboard from '../Pages/Dashboard'
import {rest} from 'msw'
import {setupServer} from 'msw/node'
import { Provider } from 'react-redux'
import { configureStore } from '@reduxjs/toolkit'
import { PostsSlice } from '../Redux/reducers'


const postsResponse = rest.get("https://jsonplaceholder.typicode.com/posts",(req,res,ctx)=>{
    console.log('this line never runs')
    return res(
        ctx.json([{id:1,userId:1,title:"hello world",body:"hola hola"}])
    )
})
const handlers = [postsResponse]
const server = new setupServer(...handlers)
beforeAll(()=>server.listen())
afterEach(()=>server.resetHandlers())
afterAll(()=>server.close())


// Redux specific-->
let store = configureStore({
    initialState:[],
    reducer : PostsSlice.reducer,
})
const MockedComponent = ({children})=>{
    return (
        <Provider store={store}>
            <BrowserRouter>
            {children}
            </BrowserRouter>
        </Provider>
    )
}

describe("Dashboard Page Test",()=>{
    test("should render hello world ",async()=>{
        render(<MockedComponent><Dashboard /></MockedComponent>);
        const element =  await findByText("hello world")
        expect(element).toBeInTheDocument();
    })

})

I'm getting the following error

 ● Dashboard Page Test › should render hello world 

    TypeError: Cannot read property 'map' of undefined

      42 |       
      43 | <Grid sx={{padding:2}}  container spacing={4}>
    > 44 |     {posts.map(item=>(
         |            ^
      45 |   <Grid item xs={12} md={8} lg={4} xl={2} key={item.id}  >
      46 |     <div className='postitems' onClick={()=>handleNavigation(item.id)} >
      47 |       <PostItem title={item.title}  />

i tried msw with 2 react app, one is this and other was pretty simple without redux. it failed in both.

tried whatwg-fetch didn't worked. tried await with fetch didn't worked tried waitForElementToBeRemoved also didn't worked.

Thanks in advance.

Edit: the code for dashboard component

import { CircularProgress, Grid } from '@mui/material'
import React,{useEffect} from 'react'
import { useDispatch } from 'react-redux'
import { useNavigate } from 'react-router-dom'
import PostItem from '../Components/PostItem'
import { PostsType } from '../Helper/interfaces'
import { useAppDispatch, useAppSelector } from '../Hooks/reduxhooks'
import useGetError from '../Hooks/useGetError'
import useGetPosts from '../Hooks/useGetPosts'
import useGetStatus from '../Hooks/useGetStatus'
import { FetchPosts } from '../Redux/reducers'

const Dashboard: React.FC = () => {
  let dispatch = useAppDispatch()
  let navigate = useNavigate()
  let posts = useGetPosts()
  const status = useGetStatus()
  const error = useGetError()

  const handleNavigation:(id:number)=>void = (id)=>{
      navigate(`/posts/${id}`)
  }
  useEffect(()=>{
    if (status === 'idle'){
      dispatch(FetchPosts())
    }
    
  },[])

  if(status === 'loading'){
    return <CircularProgress color='success' />
  }

  if (status === 'failed'){
    return <div>{error}</div>
  }


  return (
    <div>
      <h1>Dashboard</h1>
      
<Grid sx={{padding:2}}  container spacing={4}>
    {posts.map(item=>(
  <Grid item xs={12} md={8} lg={4} xl={2} key={item.id}  >
    <div className='postitems' onClick={()=>handleNavigation(item.id)} >
      <PostItem title={item.title}  />
    </div>
  </Grid>
    ))}
</Grid>
    </div>
  )
}

export default Dashboard
  • Can you show the actual code for this component? Most likely you're not handling the case where `data` is undefined. – markerikson Jun 29 '22 at 03:25

2 Answers2

0

After experimenting for few days with it i found the mistake. if anyone else isn't getting the response back from msw this can help you.

msw is not returning any response because i'm overriding the store.

what you need to do is use the rtlRenderer with custom provider (visit redux testing section for more details on this)

and in the test case don't provide any provider. Most probably you'll be using react-router-dom so make sure you're providing it to the component and everything will work perfectly.

Also for json placeholder api i had to use the complete url that is "https://jsonplaceholder.typicode.com/posts" but in the docs it is suggested to use only "/posts".

0

store.ts configuration must be changed as below and also public directory should contain mockServiceWorker.js

import { combineReducers, configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'

import counterReducer from './features/counter/counterSlice'
import { docsApi } from './services/docs'

const rootReducer = combineReducers({
  counter: counterReducer,
  [docsApi.reducerPath]: docsApi.reducer,
})

export const store = configureStore({
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(docsApi.middleware),
  reducer: rootReducer,
  preloadedState: {}
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

setupListeners(store.dispatch)