I have seen this in 2 different code bases now and am stumped because it works fine in the actual browser but not the tests: if a component uses the useParams
hook, the hook throws an error in the test:
Error: Uncaught [TypeError: Cannot destructure property
accountId
of 'undefined' or 'null'.]
I am using React Functional Component, React Testing Library and React-Router:
// component:
const Overview: FC = () => {
const location = useLocation();
console.log(location) // see below
const { accountId } = useParams();
console.log(accountId) // see below
... rest
}
the console logs appear to have found the params properly:
console.log src/screens/Overview/index.tsx:66 accountId: nodata
console.log src/screens/Overview/index.tsx:64 location: { pathname: '/mix/overview/nodata', search: '', hash: '', state: undefined, key: 'bn6zvv' }
// testing setup with wrapper as recommended in the RTL docs
function renderWithProviders(
ui,
{
route = '/',
params = routes.root,
history = createMemoryHistory({ initialEntries: [route] }),
} = {},
apolloMocks
) {
console.log("route:", route) // see below
console.log("params:", params) // see below
return {
...render(
<Provider store={mockProviderStore}>
<Router history={history}>
<MockedProvider mocks={apolloMocks}>
<Route path={params}> // tried to set path to "params" not "route" so the slug of /url/:accountId is properly set
{ui}
</Route>
</MockedProvider>
</Router>
</Provider>
),
history,
};
}
the result of the console log in the test-utils file looks correct:
console.log src/test-utils/index.js:19 route: /mix/overview/nodata
console.log src/test-utils/index.js:20 params: /mix/overview/:accountId
// test itself
test('it works', async () => {
const { findByText } = renderWithProviders(<Overview />, {
route: routes.overview('1234567890'), // => path/1234567890
params: routes.overview(), // => path/:accountId
});
await findByText('loading configs...');
await waitForElementToBeRemoved(() => getByText('loading configs...'));
await findByText('View your stuff now. It works!');
});
I am trying the recommended work around from #kentdodds to use await
in the tests which lets state changes to settle correctly.
What is it about the React-Router hook which isn't picking up the route params correctly?
You could also mock query parameters using Jest like so :
import ReactRouter from 'react-router'
describe(() => {
test('...', () => {
jest.spyOn(ReactRouter, 'useParams').mockReturnValue({ id: '1234' });
// id = 1234 in your tested component
});
});
See https://stackoverflow.com/a/58180976/2295549 to mock more of react-router.
Make sure, even if you have a wrapper with Router/Route like suggested, that the component you want to test gets wrapped with a Route with params:
<Route path="/mix/config/:param1/:param2/:param3" >
<Config />
</Route>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With