Cannot mock returned value of react hook with different values for each unittest

Viewed 118

My component below:

import { VFC } from 'react';
import { useParams } from 'react-router-dom';

export const MyComponent: VFC = () => {

const { sourceTemplate } = useParams();

  return (
        <div>
          <p>
            <b>{sourceTemplate}</b>
          </p>
        </div>
  );

};

My test class below:

import { render } from '@testing-library/react';
import { MyComponent } from './MyComponent';

const mockFn = jest.fn();
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useParams: () => mockFn
}));

describe('MyComponent', () => {

  it('First test', async () => {
    mockFn.mockReturnValue({ sourceTemplate: 'hello' });

    render(<MyComponent />);
  })

});

I would hope to see "hello" displayed in my component but I am getting undefined. I would ideally like to have different tests with different mocked return values hence the mockFn which ideally I could set different return values for each test.

The below works fine but is less configurable for multiple tests.

import { render } from '@testing-library/react';
import { MyComponent } from './MyComponent';

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

describe('MyComponent', () => {

  it('First test', async () => {
    render(<MyComponent />);
  })

});

How can I have a single mock function where I return different values in each test for the react hook useParams?

2 Answers

If you inspect your code you will notice that in the first snippet you use

useParams: () => mockFn

while on the second

useParams: () => ({ sourceTemplate: 'hello' })

So, in the first snippet, you set useParams to a function that returns the mockFn function, while on the second you set it to a function that returns the object you want.


The first snippet should be useParams: () => mockFn() (note the () after the function name). Or even better just do useParams: mockFn.

The two example tests are different:

On the first one:

const mockFn = jest.fn();

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

You are saying that useParams returns a function (mockFn), so for this to work the useParams should be called like useParams(), and that´s not the case.

On the second one:

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

You are saying that useParams returns a object, and that´s why it work.

Setting useParams: mockFn will probably throw an error ReferenceError: Cannot access 'mockFn' before initialization.

So, the best way here is mock the return value:

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

let mockReturn = "";

jest.mock("react-router-dom", () => ({
  ...jest.requireActual("react-router-dom"),
  useParams: () => ({ sourceTemplate: mockReturn }),
}));

describe("MyComponent", () => {
  beforeEach(() => {
    mockReturn = "";
  });

  it("First test", async () => {
    mockReturn = "c";

    render(<App />);

    screen.debug();
  });
});
Related