<MyComponent
match={this.props.match}
location={this.props.location}
history={this.props.history}
The props are available already because it's running inside react router.
Now, how do I test this component without match
, location
, history
available?
Do I need to mock them or is it supposed to somehow automatically load them with some helper function?
To answer your last question, the recommended approach is to use <MemoryRouter>< *your component here* ></MemoryRouter>
in your tests. Typescript does not pick up that this component will pass the required props to your component, as such I assume it not to be a type safe approach.
This is for React Router v4 and doesn't apply to previous versions.
For a typesafe method to test components that are wrapped with the HOC withRouter
you can build the location, history and match from the react-router
and history
packages.
This example uses enzyme and snapshot testing but could just as easily be any other test.
This avoided me needing to use <MemoryRouter>
as a wrapper that typescript did not like anyhow.
// Other imports here
import { createMemoryHistory, createLocation } from 'history';
import { match } from 'react-router';
const history = createMemoryHistory();
const path = `/route/:id`;
const match: match<{ id: string }> = {
isExact: false,
path,
url: path.replace(':id', '1'),
params: { id: "1" }
const location = createLocation(match.url);
test('shallow render', () => {
const wrapper = shallow(
<MyComponent history={history}
location={location}
match={match} />
expect(wrapper).toMatchSnapshot();
CAUTION Do not use this to test implementation detail, it can be tempting but it will cause you a lot of pain should you want to refactor.
Making a helper for this would probably be the best way to make this re-usable.
import { createLocation, createMemoryHistory } from 'history';
import { match as routerMatch } from 'react-router';
type MatchParameter<Params> = { [K in keyof Params]?: string };
export const routerTestProps = <Params extends MatchParameter<Params> = {}>
(path: string, params: Params, extendMatch: Partial<routerMatch<any>> = {}) => {
const match: routerMatch<Params> = Object.assign({}, {
isExact: false,
path,
url: generateUrl(path, params),
params
}, extendMatch);
const history = createMemoryHistory();
const location = createLocation(match.url);
return { history, location, match };
const generateUrl = <Params extends MatchParameter<Params>>
(path: string, params: Params): string => {
let tempPath = path;
for (const param in params) {
if (params.hasOwnProperty(param)) {
const value = params[param];
tempPath = tempPath.replace(
`:${param}`, value as NonNullable<typeof value>
return tempPath;
Now we can just use the routerTestProps
function in our tests
const { history, location, match } = routerTestProps('/route/:id', { id: '1' });
A gentleman by the name of Timmy Huang provided a solution that involves a simple mock...
https://spectrum.chat/react/help/how-do-you-test-components-that-use-routecomponentprops~495fe95b-6925-4e7f-bfe8-65a737c5d24e?m=MTU4Mjk1MjQ4ODQ0MA==
const routeComponentPropsMock = {
history: {} as any,
location: {} as any,
match: {} as any,
I tried this using Jest and it worked. My component had this signature...
export const MyComponent: React.FC<RouteComponentProps> = ({location}:RouteComponentProps) => {
My basic test to confirm the component loads then looked like this...
function renderMyComponent() {
return render(
<MyComponent {...routeComponentPropsMock}/>
I have been looking for a good solution to this. I was hoping I could do it in the mapStateToProps function or something simular, but have not been able to do this yet.
The best I could do was mock this out and pass in the match, location and history. I used the following:
import { RouteComponentProps } from 'react-router'
import { match } from 'react-router-dom';
import {UnregisterCallback, Href} from 'history'
export function getMockRouterProps<P>(data: P) {
var location: {
hash: "",
key: "",
pathname: "",
search: "",
state: {}
var props: RouteComponentProps<P> = {
match: {
isExact: true,
params: data,
path: "",
url: ""
location: location,
history: {
length:2,
action:"POP",
location: location,
push: () => {},
replace: () => {},
go: (num) => {},
goBack: () => {},
goForward: () => {},
block: (t) => {
var temp: UnregisterCallback = null;
return temp;
createHref: (t) => {
var temp: Href = "";
return temp;
listen: (t) => {
var temp: UnregisterCallback = null;
return temp;
staticContext: {
return props;
Then in my test I did:
var routerProps = getMockRouterProps<ReduxTestComponentProps>(null);
const wrapper = mount<ReduxTestComponent, ReduxTestComponentState>(
<ReduxTestComponent
history={routerProps.history}
location={routerProps.location}
match={routerProps.match}
isLoadingTodo={false}
todos={todos}
addAsyncTodoActionDispatch={() => mockTodoAddDispatch()}
deleteTodoActionDispatch={() => mockTodoDeleteDispatch()}
I used a solution similar to another answer; construct the props using a helper.) My helper uses path-to-regexp
's compile
:
import { createLocation } from "history";
import { compile } from "path-to-regexp";
import { match } from "react-router";
* Makes RouteComponentProps.
* @param path The parameterized react-router path: `/p/:id`.
* @param params the parameters to substitute in the path: `{id: "123"}`.
export function makeRouteComponentProps<T extends Record<string, string>>(
path: string,
params: T
const toUrl = compile(path, { encode: encodeURIComponent });
const url = toUrl(params);
const match: match<T> = {
isExact: false,
path,
params,
const location = createLocation(match.url);
return { match, location };
Use it like this in a test:
const history = createMemoryHistory();
const { location, match } = makeRouteComponentProps(
"p/:rootTargetId/:slug",
rootTargetId: proposition.id,
slug: toSlug(proposition.text),
// Act
const { container } = renderWithProviders(
<JustificationsPage
rootTargetType={"PROPOSITION"}
history={history}
location={location}
match={match}
{ history }
Where renderWithProviders
is like suggested in the redux docs:
import React, { PropsWithChildren } from "react";
import { render } from "@testing-library/react";
import type { RenderOptions } from "@testing-library/react";
import type { PreloadedState } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
import { Router } from "react-router-dom";
import { createMemoryHistory, History } from "history";
import { persistStore, PersistorOptions } from "redux-persist";
import type { AppStore, RootState } from "./setupStore";
import { setupStore } from "./setupStore";
interface ExtendedRenderOptions extends Omit<RenderOptions, "queries"> {
preloadedState?: PreloadedState<RootState>;
store?: AppStore;
persist?: boolean;
history?: History<any>;
export function renderWithProviders(
ui: React.ReactElement,
preloadedState = {},
store = setupStore(preloadedState),
persist = true,
history = createMemoryHistory(),
...renderOptions
}: ExtendedRenderOptions = {}
const persistor = persistStore(store, {
manualPersist: true,
} as PersistorOptions);
if (persist) {
persistor.persist();
function Wrapper({
children,
}: PropsWithChildren<Record<string, unknown>>): JSX.Element {
return (
<Provider store={store}>
<Router history={history}>{children}</Router>
</Provider>
return {
store,
persistor,
history,
...render(ui, { wrapper: Wrapper, ...renderOptions }),
Regarding createLocation
missing from history@5
, this comment suggests that parsePath
could be used as a replacement, but I didn't look into it since I'm still on v4.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.