Testing (RTK) Redux toolkit in React / Nextjs application
Tags: testing, redux, react, reduxtoolkit
Published: 2021-06-23 09:20:26

> In brief we will be just using `@reduxjs/toolkit` and we will be creating a custom `Render` function which lets you pass store data without mocking **useSelector** from `react-redux` or any external libs

{% youtube 0WhGneB2s3Y %}


This article starts with a quick crash course on redux toolkit with respect to React. Then we also **write test for the imaginary react component**.

### Lets start

To have Redux to any react application, you need to wrap your root App component with Provider.

Below is common app.ts template in a Nextjs application
- Not having types for the sake of brevity

### app.ts
```js

import { Provider } from 'react-redux'
import { store } from '../redux/store'

const App = ({ Component, pageProps }) => {

 return (
  <Provider store={store}>
   <Component {...pageProps} />
  </Provider>
 )
}

```

Now that we have basic Root App component we also gota have a Store which actually configure the Redux and reducers. aka createStore.


### redux/store.ts
```js

import { configureStore } from '@reduxjs/toolkit'
import { userSelector, userStateSlice } from './userStateSlice'

export const reducers = {
  user: userStateSlice.reducer
}

// configureStore helps you createStore with less fuss
export const store = configureStore({
  reducer: reducers
})

export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>

// e.g. to call thunk
// store.dispatch(loadUserAsync())
```



### userStateSlice.ts
```ts

import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { RootState } from './store'
import { getAuth } from '../auth/getAuth'

interface UserState {
  loaded: boolean
  apiHost?: string
  username?: string
}

const initialState: UserState = { loaded: false }

export const userStateSlice = createSlice({
  name: 'env',
  initialState,
  reducers: {
    loadUser: (state, action: PayloadAction<UserState>) =>
      (state = { ...action.payload, loaded: true })
  }
})

// this internally uses Thunk
// store.dispatch(loadUserAsync())
export const loadUserAsync = () => dispatch =>
  getAuth().then(user => dispatch(userStateSlice.actions.loadUser(user)))

export const userSelector = (state: RootState) => state.env

```

### redux-hooks.ts
```ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
import { AppDispatch, RootState } from './redux'

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

export const useAppDispatch = () => useDispatch<AppDispatch>()

```


Now we are in our imaginary React / nextjs component
where we consume Redux store

### UserLandingPage component

```js
import { useAppSelector } from '#src/redux/redux-hooks'
import { userSelector } from '#src/redux/userStateSlice'
...

const UserLandingPage = ({isAuthenticated}) => {

  // useAppSelector is just a typescript wrapper around useSelector

  const { user } = useAppSelector(userSelector)

  useEffect(() => {
    if (isAuthenticated && env?.apiHost) {
      fetchUserOrders(env.apiHost)
    }
  }, [isAuthenticated, env])

 return (
  <ContentWrapper>
    ...
  </ContentWrapper>
 )
}

```

Now the main part, where we write boilerplate test for the above Component

### UserLandingPage -> spec.ts
```ts
import { renderWithStore } from '#test/render-with-store'

describe('<UserLandingPage>', () => {
 const customInitialState = {
   user: {
    loaded: true,
    apiHost: 'https://localhost:9000'
    username: 'ajinkyax'
   }
 }
 it('renders', async () => {
  const { getAllByRole, getByText } = renderWithStore(<UserLandingPage {...props} />, customInitialState)
  ...
 })
})

```


## renderWithStore

Now the meat of this testing is `renderWithStore` which allows us to pass an initial store state and also prevents us from passing Provider to render. No more duplication of reducers for testing.

Also saves us from mocking useSelector



### render-with-store.tsx
```ts
import { configureStore } from '@reduxjs/toolkit'
import { Provider } from 'react-redux'

import { render } from '@testing-library/react'

import { reducers, RootState } from '../src/redux/store'

const testStore = (state: Partial<RootState>) => {
  return configureStore({
    reducer: reducers,
    preloadedState: state
  })
}

export const renderWithStore = (component, initialState) => {
  const Wrapper = ({ children }) => (
    <Provider store={testStore(initialState)}>{children}</Provider>
  )
  return render(component, { wrapper: Wrapper })
}

```


Hope this was helpful, let me know in the comments if you get [stuck anywhere](twitter.com/ajinkyax).