Async Thunk error running tests
See original GitHub issueHello everybody, I’m writing some tests for my react app, however i’m receiving an error:
TypeError: Cannot read properties of undefined (reading 'pending')
15 | extraReducers: (builder) => {
16 | builder
> 17 | .addCase(fetchCategories.pending, (state, { meta }) => {
| ^
18 | const { requestId } = meta
19 |
20 | if (state.loading === 'idle') {
If i have two or more middlewares running, i don’t have this problem, but if i have less, i get this error. I can’t find a solution for this, hope anyone can save me, in development my code runs perfectly.
This is my code:
// store.ts
import { combineReducers, configureStore } from '@reduxjs/toolkit'
import autoLoginMiddleware from 'features/middlewares/auto-login.middleware'
import {
persistReducer,
persistStore,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import logger from 'redux-logger'
import userReducer from 'features/user/user.slice'
import cartReducer from 'features/cart/cart.slice'
import categoriesReducer from 'features/categories/category.slice'
const persistConfig = {
key: 'root',
storage,
whitelist: ['cart'],
}
const persistedReducer = persistReducer(
persistConfig,
combineReducers({
user: userReducer,
cart: cartReducer,
categories: categoriesReducer,
})
)
export const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(logger, autoLoginMiddleware),
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const persistor = persistStore(store)
export default store
// cart-dropdown.test.tsx
import '@testing-library/jest-dom'
import mockCartItems from 'mocks/cart-items'
import { render, screen, fireEvent, act } from 'utils/test'
import CartDropdown from './cart-dropdown.component'
import store from 'app/store'
import { setCartItems } from 'features/cart/cart.slice'
const mockedUseNavigate = jest.fn()
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: () => mockedUseNavigate,
}))
describe('[Component] CartDropdown', () => {
beforeEach(() => render(<CartDropdown />))
it('Starts with cart empty', () => {
expect(screen.getByText(/Your cart is empty/i)).toBeInTheDocument()
})
it('Showing items when added', () => {
act(() => {
store.dispatch(setCartItems(mockCartItems))
})
expect(screen.getAllByRole('img').length).toBe(3)
})
it('Button redirecting to checkout', () => {
const button = screen.getByRole('button')
fireEvent.click(button)
expect(mockedUseNavigate).toHaveBeenCalled()
})
})
// category.thunk.ts
import { createAsyncThunk } from '@reduxjs/toolkit'
import { apolloClient } from 'app/api'
import { GET_ALL_CATEGORIES } from 'apollo/categories.queries'
import { GET_ALL_PRODUCTS } from 'apollo/products.queries'
import type { GqlCategory, Category, GqlProduct } from './category.types'
import { getCategoryProducts } from 'helpers/products'
import { RootState } from 'app/store'
export const fetchCategories = createAsyncThunk<
Category[],
undefined,
{ state: RootState }
>('categories/fetchCategories', async (_, { getState, requestId }) => {
const { currentRequestId, loading } = getState().categories
if (loading !== 'pending' || requestId !== currentRequestId) return
const {
data: { Categories },
} = await apolloClient.query({ query: GET_ALL_CATEGORIES })
if (Categories) {
let {
data: { Products: dbProducts },
} = await apolloClient.query({ query: GET_ALL_PRODUCTS })
// eslint-disable-next-line @typescript-eslint/no-unused-vars
dbProducts = dbProducts.map(({ __typename, ...product }: GqlProduct) => {
return { ...product }
})
const categoriesMap = Categories.map(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
({ __typename, active, ...category }: GqlCategory) => {
if (active === true)
return {
...category,
items: getCategoryProducts(category, dbProducts),
}
}
)
return categoriesMap
}
})
// category.slice.ts
import { createSlice } from '@reduxjs/toolkit'
import { fetchCategories } from './category.thunk'
import type { CategorySliceState } from './category.types'
const categorySlice = createSlice({
name: 'category',
initialState: {
categories: [],
loading: 'idle',
error: null,
currentRequestId: undefined,
} as CategorySliceState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchCategories.pending, (state, { meta }) => {
const { requestId } = meta
if (state.loading === 'idle') {
state.loading = 'pending'
state.error = null
state.currentRequestId = requestId
}
})
.addCase(fetchCategories.fulfilled, (state, action) => {
const { requestId } = action.meta
if (
state.loading === 'pending' &&
state.currentRequestId === requestId
) {
state.loading = 'idle'
state.error = null
state.categories = action.payload
state.currentRequestId = undefined
}
})
.addCase(fetchCategories.rejected, (state, action) => {
const { requestId } = action.meta
if (
state.loading === 'pending' &&
state.currentRequestId === requestId
) {
state.loading = 'idle'
state.categories = []
state.currentRequestId = undefined
state.error = action.error
}
})
},
})
export default categorySlice.reducer
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:7 (1 by maintainers)
Top Results From Across the Web
Async thunk doesn't get executed when running trough test files
I am currently working on a migration, removing react context and using reduxt toolkit. Basically created the basics for it:.
Read more >How to Write Unit Tests for Asynchronous Redux Thunks in ...
Step 1: Analyze dependencies · Step 2: Define expected behaviours · Step 3: Mock dependencies · Step 4: Implement the tests · Step...
Read more >createAsyncThunk - Redux Toolkit
createAsyncThunk. Overview. A function that accepts a Redux action type string and a callback function that should return a promise.
Read more >Async waits in React Testing Library - Reflect.run
Handling when the API call returns with an error or timeout via Promise chaining or a try/catch block and async/await.
Read more >Testing and error handling patterns in Next.js - LogRocket Blog
Check out these different ways to run tests and handle errors in Next.js ... export const getStaticProps = async () => { try...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Ah. @jzorzetti the issue here really has nothing to do with Apollo at all. The problem is the
import { store } from './store'
.This is specifically why we tell users you should not directly import the store into other app files - it’s likely to cause circular import issues!
If you absolutely must have access to the store in this file, use the “setter/injection” approach shown in our FAQ here:
https://redux.js.org/faq/code-structure#how-can-i-use-the-redux-store-in-non-component-files
After removing the type import from app.ts, the only circular dep remaining is from the apollo client, however, i need apollo client to query my stuff (thats why async thunks are needed i think). Can’t I use apollo with Redux?