4

I have to test a component which renders based on the country and language in the url path params. So I want to the if the components are rendered properly based on the change in params.

I am mocking the useParams and setting some required value which works for most of the tests. Now for a specific case, I need to change the param.

   jest.mock('react-router-dom', () => ({
      ...jest.requireActual('react-router-dom'),
      useParams: () => ({
        language: 'IT'
      })
    }));

How do I override the language in test ?

Thank you

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
Arjun K R
  • 427
  • 1
  • 6
  • 25

3 Answers3

2

Instead of mocking useParams you can use <MemoryRouter initialEntries>, for example:

MyComponent.js

import { useParams } from "react-router-dom";

function MyComponent() {
  const { language } = useParams();
  return (
    <div>
      <h2>My Component page</h2>
      <p>language: {language}</p>
    </div>
  );
}

MyComponent.test.js

import { MemoryRouter } from "react-router-dom";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";

test("Home: Go to `MyComponent: en`", async () => {
  render(
    <MemoryRouter initialEntries={["/"]}>
      <App />
    </MemoryRouter>
  );

  expect(screen.getByText("Home page")).toBeInTheDocument();

  fireEvent.click(screen.getByText("My Component: en"));

  expect(screen.getByText("language: en")).toBeInTheDocument();
  expect(screen.queryByText("language: fr")).not.toBeInTheDocument();
});

test("MyComponent: en", async () => {
  render(
    <MemoryRouter initialEntries={["/myComponent/en"]}>
      <App />
    </MemoryRouter>
  );

  expect(screen.getByText("language: en")).toBeInTheDocument();
});

test("MyComponent: fr", async () => {
  render(
    <MemoryRouter initialEntries={["/myComponent/fr"]}>
      <App />
    </MemoryRouter>
  );

  expect(screen.getByText("language: fr")).toBeInTheDocument();
});

Online demo

Edit eloquent-tu-imqcbh

Fraction
  • 11,668
  • 5
  • 28
  • 48
  • This is a nice solution, I need to try this out (in the meantime I found a solution/hack) and if it works, i'll accept this answer – Strae Mar 22 '22 at 22:59
  • Can you elaborate on how this works? The first test seems like it shouldn't pass, the language is never set to "en" and your component doesn't default to anything so it should never render to show "language: en" like your expect statement says. – Duskendale Dec 15 '22 at 16:26
  • @Duskendale Have you seen the codesandbox example ? The first test starts from the home page, then a click is made on the link with the text `My Component: en` that redirects to `/myComponent/en`. – Fraction Dec 16 '22 at 08:25
1

Based on this page on the Jest docs, try the following

// create a separate mock function that you can access from tests
// NOTE: the name must start with `mock` and is case sensitive 
const mockUseParams = jest.fn().mockReturnValue({
  language: 'IT',
});

// mock the module using the mock function created above
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => mockUseParams(),
}));

it('should behave differently when the params change', () => {
  mockUseParams.mockReturnValueOnce({
    language: 'EN',
  });

  // test implementation
});
berelig
  • 30
  • 6
MkMan
  • 1,779
  • 1
  • 14
  • 27
0

yes Include the Router in your App component; Always render the App component in your tests (never child components like Locations); Navigate to your pages in tests by finding and clicking links on the page The positives of this approach: you don’t need to read the rest of this post (and your test setup will be less complicated). The negatives: you can’t immediately load a routing history (the current page and previous pages) in test setup; you need to go through all the user interactions to build the history.