How to test a React component with RouteComponentProps? How to test a React component with RouteComponentProps? reactjs reactjs

How to test a React component with RouteComponentProps?


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 hereimport { 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()}                />      );