Как вызвать setState на издевательском крючке

#reactjs #react-hooks #react-testing-library #swr

Вопрос:

У меня есть компонент, который использует крюк, который извлекает данные с сервера, и я издевался над этим крючком, чтобы вернуть свои данные тестирования. Теперь, если mutate функция (возвращаемая крючком) вызывается, обычная реализация снова извлекает данные и вызывает повторный рендеринг (я использую swr , вот mutate ссылка).

Как запустить повторный рендеринг / setState на издевательском крючке?

Что я хочу проверить: просто, если пользователь создает элемент, список элементов должен быть повторно извлечен и отображен.

Код для иллюстрации проблемы:

 const existing = [...]
const newlyCreated = [...];

useData.mockReturnValue({ data: [existing] });

const { getByRole, findByText } = render(<MyComponent />);
const form = getByRole("form");
const createButton = within(form).getByText("Create");

useData.mockReturnValue({ data: [existing, newlyCreated] });

// createButton.click();
// Somehow trigger re-render???

for (const { name } of [existing, newlyCreated]) await findByText(name);
 

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

1. Рассматривали ли вы возможность издеваться над ответом API, а не над useData крючком?

2. @juliomalves да, у меня есть. Но наличие специального крючка для извлечения данных и насмешек, которые были рекомендованы в SO и в вопросах swr . Сначала я пытался издеваться swr , но на самом деле ничего не вышло. Я мог бы посмеяться fetch , но я не думаю, что это правильный путь, не так ли? Особенно потому, что это детали реализации.

3. Я имел в виду издевательство над ответами API с помощью перехватчиков запросов, таких как nock или msw .

4. @juliomalves, это предпочтительнее, чем издеваться над крючком? Я хотел бы прочитать некоторые ресурсы, прежде чем менять весь свой код и в конечном итоге замечать, что что-то не так, как должно быть 😅. Я быстро просмотрел readme связанных библиотек, и правильно msw ли это, что в моем случае это не сработало бы? Поскольку тестирование выполняется не в реальной среде браузера, а в js-dom ( msw используйте работников службы).

5. Я только что заметил, что msw в нем есть раздел о выполнении в узле, так что это не должно быть проблемой.

Ответ №1:

Вам не нужно запускать повторный рендеринг в своем тесте.

Проблема в том, что кнопка в вашем пользовательском интерфейсе изменяет данные, но, поскольку вы издеваетесь useData , этой мутации не происходит.

Вы можете просто добавить mutate() в свой макет и назначить ему функцию макета.

Вам не нужно модульно тестировать внутреннюю работу собственных SWR mutate() — это уже охвачено их собственным проектом.

 const existing = ["Mercury", "Venus", "Earth", "Mars"];
const newItem = "Jupiter";

test("Create button should call mutate() with correct value", async () => {
  const mutate = jest.fn();
  jest.spyOn(useData, "default").mockImplementation(() => ({
    data: existing,
    mutate
  }));
  let resu<
  act(() => {
    result = render(<Form />);
  });
  await waitFor(() => {
    existing.forEach((item) => {
      expect(result.getByText(item)).toBeInTheDocument();
    });
  });
  const input = result.container.querySelector("input");
  fireEvent.change(input, { target: { value: newItem } });
  const createButton = result.getByText("Create");
  createButton.click();
  expect(mutate).toBeCalledWith([...existing, newItem], false);
});
 

Пример на CodeSandbox

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

1. Действительно, мне не нужно проверять, что mutate это работает, но я хочу проверить, что мой экран отображает правильные новые данные. Я полагаю, что я мог бы проверить, был ли вызван mute, и добавить второй тест, который охватывает случай после мутации.

2. Учитывая асинхронный характер моего mutate вызова, тест будет выполнен до вызова функции. Это (как я теперь помню) основная причина, по которой я хотел изменить состояние, чтобы я мог ожидать изменений на экране.