Издевательство над пользовательским хуком React с помощью Jest

#reactjs #jestjs #mocking #react-hooks

#reactjs #jestjs #издевательство #реагирующие хуки

Вопрос:

Мне нужно издеваться над моим пользовательским хуком при модульном тестировании компонента React. Я прочитал несколько руководств и ответов stackoverflow на эту простую задачу, но не смог ее правильно реализовать.

Моя самая простая возможная настройка для одного теста выглядит следующим образом:

 // TestComponent.js

import React from "react";
import useTest from "./useTest";

const TestComponent = () => {
  const { state } = useTest("initial_value");

  return <div>{state}</div>;
};

export default TestComponent;
 
 // useTest.jsx - simple custom hook

import React, { useState } from "react";

const useTest = (initialState) => {
  const [state] = useState(initialState);
  return { state };
};

export default useTest;

 
 // TestComponent.test.jsx - my test case

import React from "react";
import { render } from "@testing-library/react";
import TestComponent from "./TestComponent";

jest.mock("./useTest", () => ({
  useTest: () => "mocked_value",
}));

test("rendertest", () => {
  const component = render(<TestComponent />);
  expect(component.container).toHaveTextContent("mocked_value");
});

 

Итак, я пытаюсь издеваться useTest над пользовательским хуком, чтобы вернуть «mocked_value» вместо «initial_value» из реального пользовательского хука. Но приведенный выше код просто выдает мне эту ошибку:

 TypeError: (0 , _useTest.default) is not a function

      3 | 
      4 | const TestComponent = () => {
    > 5 |   const { state } = useTest("initial_value");
        |                     ^
      6 | 
      7 |   return <div>{state}</div>;
      8 | };
 

Я также пробовал:

 import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockImplementation(() => "mocked_value");
 
 import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockReturnValue("mocked_value");
 

Но оба дают мне ошибку Cannot spy the useTest property because it is not a function; undefined given instead .

Как мне реализовать этот тест?

Ответ №1:

Я отвечаю сам себе. Таким образом, это работает:

 jest.mock("./useTest", () => ({
  useTest: () => ({ state: 'mocked_value' }),
}));
 

И если я хочу использовать экспорт по умолчанию в пользовательском ху:

 jest.mock("./useTest", () => ({
  __esModule: true,
  default: () => ({ state: 'mocked_value' }),
}));
 

Кроме того, если я хочу также использовать setState метод в своем ху и экспортировать его, я могу издеваться над ним следующим образом:

 const mockedSetState = jest.fn();

jest.mock("./useTest", () => ({
  useTest: () => ({ state, setState: mockedSetState }),
}));
 

И теперь можно проверить, был ли setState вызван один раз:

 expect(mockedSetState).toHaveBeenCalledTimes(1);
 

Ответ №2:

Поскольку вы используете export default useTest модуль in useTest, он ожидает получить ссылку на эту функцию из атрибута по умолчанию в вашем макете.

Попробуйте это:

 jest.mock("./useTest", () => ({
  default: () => "mocked_value",
}));
 

Если вы хотите избежать путаницы, вы можете попробовать export const useTest = ... в модуле useTest, а затем import { useTest } from './useTest' в вашем компоненте. При использовании этого подхода нет необходимости изменять ваш тест.

Комментарии:

1. Спасибо за совет, но это тоже не работает. Я попытался изменить «useTest» на «default» в jest.mock функции, а также попытался экспортировать useTest хук с export default useTest... помощью, но ничего не работает. Всегда одна и та же ошибка TypeError: (0 , _useTest.default) is not a function .

2. Я заставил его работать, слегка изменив его (см. Мой собственный Ответ), но спасибо за этот совет по экспорту, это было ценно!

Ответ №3:

Для тех, кто использует правильный хук, это можно сделать так

 import * as useFetchMock from "../../../../hooks/useFetch";

it("should show loading component on button when state is loading", () => {
    jest.spyOn(useFetchMock, "default").mockReturnValue({
        result: null,
        message: null,
        status: "pending",
        loading: true,
        fetch: jest.fn(),
    });

    render(<Component />);

    expect.assertions(1);

    expect(screen.getByTestId(testIdComponentB)).toBeInTheDocument();
});
 

импортируйте свой пользовательский хук напрямую и используйте «по умолчанию» для прямого взаимодействия