Instead of mocking useOutletContext
I used composition and React Router's MemoryRouter
to mimic the behaviour of the real app.
I created a RenderRouteWithOutletContext
component that should be used to wrap the component you're testing.
// RenderRouteWithOutletContext.tsx
import { ReactNode } from 'react';
import { MemoryRouter, Outlet, Route, Routes } from 'react-router-dom';
interface RenderRouteWithOutletContextProps<T = any> {
context: T;
children: ReactNode;
}
export const RenderRouteWithOutletContext = <T,>({
context,
children,
}: RenderRouteWithOutletContextProps<T>) => {
return (
<MemoryRouter>
<Routes>
<Route path="/"element={<Outlet context={context as T} />}>
<Route index element={children} />
</Route>
</Routes>
</MemoryRouter>
);
};
And in your test file:
// YourComponent.test.tsx
import { screen, cleanup, render } from '@testing-library/react';
import { describe, expect, it, afterEach } from 'vitest';
import { RenderRouteWithOutletContext } from './RenderRouteWithOutletContext';
const mockOutletContextData: any = {
foo: 'bar',
};
afterEach(() => {
cleanup();
});
describe('PersonOverview', () => {
it('should render as expected', () => {
render(
<RenderRouteWithOutletContext context={mockOutletContextData}>
<YourComponent />
</RenderRouteWithOutletContext>,
);
const component = screen.getByTestId('component-test-id');
expect(component).toBeInTheDocument();
});
});
Notice I'm using Vitest above but the Jest version of this is almost exactly the same.
This solution is great because it is very similar to how your app is actually used.
<RenderRouteWithOutletContext>
– Fleet