I currently encountered a case that I want to write a unit test for my custom hook, said useMyHook
in which called useLocation
a simple case of the hook is sth. like this:
import { useState, useEffect } from 'react'
import { useLocation } from 'react-router-dom'
const useMyHook = () => {
const { pathname } = useLocation()
const { myState, setMyState } = useState()
useEffect(() => {
switch(pathname) {
case '/':
setMyState('a')
break
case '/b':
setMyState('b')
break
default:
setMyState('c')
break
}
}, [pathname])
return myState
}
my test is like:
import { renderHook } from '@testing-library/react-hooks'
import useMyHook from '../useMyHook'
describe('useEventId', () => {
it('path matches roleSelection', () => {
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'), // since you still want to use the actual MemoryRouter
useLocation: () => ({
pathname: '/',
}),
}))
const state = renderHook(() => useMyHook())
expect(state).toBe('a')
})
})
However, I kept getting this error:
useLocation() may be used only in the context of a component.
Can anyone point out what’s the proper way to test this kind of custom hook?
2
Answers
You need to wrap your test environment inside a router context ( same as you do in a real router example ), but using the
MemoryRouter
component.So, you need to wrap your hook in a functional component that provides the necessary context.
This is a full example of the code.
Don’t mock
react-router-dom
module.useState()
hook returns an array, not an object.You should wrap the hook with
<MemoryRouter/>
componente.g.
useMyHook.ts
:useMyHook.test.tsx
:Test result: