Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Unit test for React component containing useRouteMatch

Having the following component:

import React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useRouteMatch, Link } from 'react-router-dom';

interface MyComponentProps {
  myId?: string;
  link?: string;
}

export const MyComponent: React.FunctionComponent<MyComponentProps> = ({
  myId = 'default-id',
  link,
  children
}) => {
  const [myOutlet, setMyOutlet] = useState<HTMLOListElement>();
  const match = useRouteMatch();

  useEffect(() => {
    const outletElement = document.getElementById(myId) as HTMLOListElement;
    if (outletElement) {
      setMyOutlet(outletElement);
    }
  }, [myId]);

  if (!myOutlet) {
    return null;
  }

  return createPortal(
    <li>
      <Link to={link || match.url}>{children}</Link>
    </li>,
    myOutlet
  );
};

export default MyComponent;

I want to write unit tests using React Testing Library for it, the problem is that it keeps throwing an error because of useRouteMatch.

Here is my code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import { render, screen } from '@testing-library/react';

import { MyComponent } from './my-component';

describe('MyComponent', () => {
  const testId = 'default-id';
  const link = '/route';
  it('should render MyComponent successfully', () => {
    const element = render(<MyComponent myId={testId} link={link} />);
    expect(element).toBeTruthy();
  });
});

The error appears at the line with const match = useRouteMatch();, is there a way to include this part in the test?

>Solution :

You should use <MemoryRouter>:

A <Router> that keeps the history of your “URL” in memory (does not read or write to the address bar)

Provide mock locations in the history stack by using the initialEntries props.

Then, use <Route> component to render some UI when its path matches the current URL.

The following example, assuming that the location pathname in the browser current history stack is /one, <Route>‘s path prop is also /one, The two matching, rendering MyComponent.

E.g.

my-component.tsx:

import React, { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useRouteMatch, Link } from 'react-router-dom';

interface MyComponentProps {
  myId?: string;
  link?: string;
}

export const MyComponent: React.FunctionComponent<MyComponentProps> = ({ myId = 'default-id', link, children }) => {
  const [myOutlet, setMyOutlet] = useState<HTMLOListElement>();
  const match = useRouteMatch();
  console.log('match: ', match);

  useEffect(() => {
    const outletElement = document.getElementById(myId) as HTMLOListElement;
    if (outletElement) {
      setMyOutlet(outletElement);
    }
  }, [myId]);

  if (!myOutlet) {
    return null;
  }

  return createPortal(
    <li>
      <Link to={link || match.url}>{children}</Link>
    </li>,
    myOutlet
  );
};

export default MyComponent;

my-component.test.tsx:

import React from 'react';
import { render, screen } from '@testing-library/react';

import { MyComponent } from './my-component';
import { MemoryRouter, Route } from 'react-router-dom';

describe('MyComponent', () => {
  const testId = 'default-id';
  const link = '/route';
  it('should render MyComponent successfully', () => {
    const element = render(
      <MemoryRouter initialEntries={[{ pathname: '/one' }]}>
        <Route path="/one">
          <MyComponent myId={testId} link={link} />
        </Route>
      </MemoryRouter>
    );
    expect(element).toBeTruthy();
  });
});

test result:

 PASS  examples/70077434/my-component.test.tsx (8.433 s)
  MyComponent
    ✓ should render MyComponent successfully (46 ms)

  console.log
    match:  { path: '/one', url: '/one', isExact: true, params: {} }

      at MyComponent (examples/70077434/my-component.tsx:13:11)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------|---------|----------|---------|---------|-------------------
All files         |    87.5 |    28.57 |     100 |   86.67 |                   
 my-component.tsx |    87.5 |    28.57 |     100 |   86.67 | 18,26             
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.951 s, estimated 9 s

package versions:

"react": "^16.14.0",
"react-router-dom": "^5.2.0"
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading