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 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' });

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)