DEV Community

Siyi Le
Siyi Le

Posted on

Quick template to test Redux Tool Kit and React Router with Jest

Redux Tool Kit and React Router is very useful in React development process. Unfortunately, there aren't too much information/post about boilerplate that directly fitting redux tool kit. But I'm sharing my own quick test utils function and setup in testing with typescript.

Assuming we are using the React-Router and Redux Tool Kit in our application. And we select the Jest and React Testing Library(RTL) as our test framework.

Start wrapping!

First function we need is a function that wrapper any component into it, and then we can use render method in RTL to directly render component and also cover the reducer/actioncreator in redux.

import { configureStore, Store } from '@reduxjs/toolkit'
import { ConnectedRouter } from 'connected-react-router'
import React, { ComponentType, ReactElement } from 'react'
import { Provider } from 'react-redux'
import { history, middleware, reducer } from '../app/store'

export const makeStore = (): Store => {
  return configureStore({ reducer, middleware })
}

const wrapComponent = (Component: ComponentType, store: Store | null = null, props = {}): ReactElement => {
  return (
    <Provider store={store || makeStore()}>
      <ConnectedRouter history={history}>
        <Component {...props} />
      </ConnectedRouter>
    </Provider>
  )
}

export default wrapComponent

What this function do is to provide redux and react/router context for our component. You can provide your own store, if you want to get the information in test. And props can also be manually passed in to render display component.

If we want to test our <AwesomeComponent />. We can just write such code in jest.

In our AwesomeComponent.spec.ts:

import wrapComponent from 'wrapComponent'
import { render, screen } from '@testing-library/react'
import AwesomeComponent from 'AwesomeComponent'


describe('<HostCheckoutContainer />', () => {
    beforeEach(() => {
      render(wrapComponent(AwesomeComponent))
    })

    it('should do some testing', () => {
        expect(screen.getByRole('button', { name: /submit/i })).toBeInTheDocument()
    })
}

And then... We are done! We can write test on any component we want, and they will be just running fine. But don't forget to write mockApi.ts! Our component shouldn't rely on Database, network... But our reducer will be automatically included in our test. Moreover, If you are writing integration test, just put the <App />(the very top component) in warpComponent, you can simulate page jump as well! Woo-woo, that's really cool~

It is not recommended, but you may want...

If you really want to check the redux store state in your test. You are also able to do this using the boilerplate above. All our redux state is stored in store. We can use store.getState() to check it anytime you want.

Imagine we simulate user to click a button/link and navigate to another page. We can simple check our redux store to determine the url is changed or not.

describe('<AwesomeComponent />', () => {
    it('should test redux state', () => {
        const store = makeStore()
        render(wrapComponent(HostDetailContainer, store))
        button = screen.getByRole('button', { name: /detail/i })
        expect(store.getState().router.location.pathname).toEqual('/article/1/')
    })
}

Quick url Hacks using react-router.

React router is fantastic routing tool in Single Page Application development, especially we are using some history hooks make life easier. However, we may encounter several common pattern problem using it. No worries, we can hack it!

When we doing web development, it is quite normal that we use Restful API. And in our component with routing role, we may write codes like this...

import { Route, Switch } from 'react-router'

const Routing: (): ReactElement => {
  return (
    <Switch>
      {/* Host Router */}
      <Route path="/article/:id" render={() => <AwesomeRestfulComponent />} />
    </Switch>
  )
}

In order to get the :id value, you are inclined to use hooks in your component.

const { id } = useParams()

But we don't access our component directly using url, we can't get id variable from that. Luckily, we got a quick solution. We can add such line in our AwesomeRestfulComponent.spec.ts...

import routeData from 'react-router'

describe('<AwesomeRestfulComponent />', () => {
  beforeEach(() => {
    jest.spyOn(routeData, 'useParams').mockReturnValue({ id: '1' })
    render(wrapComponent(AwesomeRestfulComponent))
  })

   // some test...
}

We are now able to use the manually added id in our component, sweet!

Test component without redux

We also write some small reusable component, and for thoses component redux store normally does not provide state for the component. Thus we hope we can use render(<NavBar />) to test it, but not in wrapped component. We can hack the useHistory hook in react-router

We have our <NavBar /> component as following...

export type Props = {
  children?: React.ReactNode
  path?: string
}

const NavBar = (props: Props): ReactElement => {
  const history = useHistory()

  const { children, path } = props

  const onClick = useCallback(() => {
    if (path) {
      history.push(path)
    } else {
      history.goBack()
    }
  }, [history, path])

  return (
    <>
      <Row>
        <Button type="icon" onClick={onClick}>
          Go to somewhere...
        </Button>
        <Heading level={3}>{children}</Heading>
      </Row>
    </>
  )
}

export default NavBar

We can hack useHistory in such way in our test...

const mockHistoryPush = jest.fn()
const mockHistoryGoBack = jest.fn()

jest.mock('react-router', () => ({
  useHistory: () => ({
    push: mockHistoryPush,
    goBack: mockHistoryGoBack,
  }),
}))

describe('<PageNavBar />', () => {
  const props: Props = {
    children: 'Detail',
    path: '/123',
  }

  it('should render properly', () => {
    render(<NavBar {...props} />)
    userEvent.click(screen.getByRole('button'))
    expect(mockHistoryGoBack).toBeCalledTimes(1)
  })
})

Hope this post help you a bit XD.
Happy coding!

Reference

https://itnext.io/react-redux-integration-tests-with-jest-enzyme-df9aa6effd13

Top comments (3)

Collapse
 
cocodrino profile image
carlos L

thank you so much, this is the best article about React Testing Library with RTK :-)...

Collapse
 
victorcarvalhosp profile image
victorcarvalhosp

Do you have the entire code on github to share?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.