Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a React component with RouteComponentProps?

I have a component that has props which extend RouteComponentProps that looks like this:

export interface RouteComponentProps<P> {   match: match<P>;   location: H.Location;   history: H.History;   staticContext?: any; } 

Now, when I use my component in the app, I pass these props to it:

<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?

like image 203
Sergei Basharov Avatar asked Oct 25 '17 08:10

Sergei Basharov


People also ask

How do I test my React components?

There are a few ways to test React components. Broadly, they divide into two categories: Rendering component trees in a simplified test environment and asserting on their output. Running a complete app in a realistic browser environment (also known as “end-to-end” tests).

How to test React components in jest?

There are various test libraries such as chai, mocha, etc. In this tutorial, we’ll be using the jest library along with reacting testing library to test out the React Components. Step 1: You will start a new project using create-react-app so open your terminal and type. Step 2: Switch to the jest-testing folder using the following command.

Why do we need testing in react development?

Maybe it’s kind of trivial in a small project, however, when your project become larger and larger, you don’t want to test every single function manually, right? It’ll be a total waste of time, and you might forget to test some special cases. That’s why we need testing. For simplicity, let’s use create-react-app to create a React project.

How do you Mock a push function in react router?

Basically, we create a mock function called mockHistoryPush to replace the actual push function. Then, we mock the module react-router-dom, which means now the mocking react-router-dom module only have one method called useHistory, and it contains a push method which is mockHistoryPush that we just created before.

How to simulate a web event in react testing library?

React Testing Library provides a function called fireEvent to simulate the web event. Here I’ll use click event as an example. In the Button component, I have a handleClick function which will increment the counter each time I click the button. And it’ll display the value of the counter in the button.


2 Answers

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' }); 
like image 100
David Barker Avatar answered Sep 28 '22 03:09

David Barker


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}/>   ); } 
like image 33
Danoz Avatar answered Sep 28 '22 03:09

Danoz