Как макетировать вызовы API, сделанные в компоненте React, тестируемом с помощью Jest - PullRequest
7 голосов
/ 12 апреля 2019

Я пытаюсь смоделировать fetch (), который извлекает данные в компонент.

Я использую это как модель для насмешек над моими выборками , но у меня возникают проблемы с тем, чтобы заставить его работать.

Я получаю эту ошибку при запуске моих тестов: babel-plugin-jest-hoist: The module factory of 'jest.mock()' is not allowed to reference any out-of-scope variables.

Есть ли способ заставить эти функции возвращать фиктивные данные вместо того, чтобы фактически пытаться делать реальные вызовы API?

код

Utils / getUsers.js

Возвращает пользователей с ролями, сопоставленными с каждым пользователем.

const getUsersWithRoles = rolesList =>
  fetch(`/users`, {
    credentials: "include"
  }).then(response =>
    response.json().then(d => {
      const newUsersWithRoles = d.result.map(user => ({
        ...user,
        roles: rolesList.filter(role => user.roles.indexOf(role.iden) !== -1)
      }));
      return newUsersWithRoles;
    })
  );

компонент / UserTable.js

const UserTable = () => {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    getTableData();
  }, []);

  const getTableData = () => {
    new Promise((res, rej) => res(getRoles()))
      .then(roles => getUsersWithRoles(roles))
      .then(users => {
        setUsers(users);
      });
  };
  return (...)
};

компонент / Тесты / UserTable.test.js

import "jest-dom/extend-expect";
import React from "react";
import { render } from "react-testing-library";
import UserTable from "../UserTable";
import { getRoles as mockGetRoles } from "../utils/roleUtils";
import { getUsersWithRoles as mockGetUsersWithRoles } from "../utils/userUtils";

const users = [
  {
    name: "Benglish",
    iden: "63fea823365f1c81fad234abdf5a1f43",
    roles: ["eaac4d45c3c41f449cf7c94622afacbc"]
  }
];

const roles = [
  {
    iden: "b70e1fa11ae089b74731a628f2a9b126",
    name: "senior dev"
  },
  {
    iden: "eaac4d45c3c41f449cf7c94622afacbc",
    name: "dev"
  }
];

const usersWithRoles = [
  {
    name: "Benglish",
    iden: "63fea823365f1c81fad234abdf5a1f43",
    roles: [
      {
        iden: "eaac4d45c3c41f449cf7c94622afacbc",
        name: "dev"
      }
    ]
  }
];

jest.mock("../utils/userUtils", () => ({
  getUsers: jest.fn(() => Promise.resolve(users))
}));
jest.mock("../utils/roleUtils", () => ({
  getRolesWithUsers: jest.fn(() => Promise.resolve(usersWithRoles)),
  getRoles: jest.fn(() => Promise.resolve(roles))
}));

test("<UserTable/> show users", () => {
  const { queryByText } = render(<UserTable />);

  expect(queryByText("Billy")).toBeTruthy();
});

Ответы [ 3 ]

3 голосов
/ 13 апреля 2019

По умолчанию jest.mock вызовы обрабатываются babel-jest ...

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

Именно поэтому фабрика модулей, переданная в jest.mock, не может ссылаться на что-либо вне себя.


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

jest.mock("../utils/userUtils", () => {
  const users = [ /* mock users data */ ];
  return {
    getUsers: jest.fn(() => Promise.resolve(users))
  };
});
jest.mock("../utils/roleUtils", () => {
  const roles = [ /* mock roles data */ ];
  const usersWithRoles = [ /* mock usersWithRoles data */ ];
  return {
    getRolesWithUsers: jest.fn(() => Promise.resolve(usersWithRoles)),
    getRoles: jest.fn(() => Promise.resolve(roles))
  };
});

Другой вариант - это смоделировать функции, используя jest.spyOn:

import * as userUtils from '../utils/userUtils';
import * as roleUtils from '../utils/roleUtils';

const users = [ /* mock users data */ ];
const roles = [ /* mock roles data */ ];
const usersWithRoles = [ /* mock usersWithRoles data */ ];

const mockGetUsers = jest.spyOn(userUtils, 'getUsers');
mockGetUsers.mockResolvedValue(users);

const mockGetRolesWithUsers = jest.spyOn(roleUtils, 'getRolesWithUsers');
mockGetRolesWithUsers.mockResolvedValue(usersWithRoles);

const mockGetRoles = jest.spyOn(roleUtils, 'getRoles');
mockGetRoles.mockResolvedValue(roles);

И еще один вариант -автоматически смоделировать модули:

import * as userUtils from '../utils/userUtils';
import * as roleUtils from '../utils/roleUtils';

jest.mock('../utils/userUtils');
jest.mock('../utils/roleUtils');

const users = [ /* mock users data */ ];
const roles = [ /* mock roles data */ ];
const usersWithRoles = [ /* mock usersWithRoles data */ ];

userUtils.getUsers.mockResolvedValue(users);
roleUtils.getRolesWithUsers.mockResolvedValue(usersWithRoles);
roleUtils.getRoles.mockResolvedValue(roles);

... и добавить смоделированный ответ к пустым фиктивным функциям.

0 голосов
/ 13 апреля 2019

Это обобщенный ответ.Сначала сделайте jest.mock(../service) внутри вашего теста.

Затем перейдите в служебный файл, где вы делаете вызовы.Создайте файл __mocks__ и внутри сделайте файл с именем, совпадающим с файлом службы.

Затем создайте функции для каждой требуемой службы, которая разрешает обещание, чтобы быть асинхронным, как обычные вызовы.

const getUser = () => promise.resolve()

Выше приведен общий пример.Если вам нужны данные, создайте объект / массив mockData по вашему выбору и обслужите их с помощью функции.

0 голосов
/ 12 апреля 2019

Вам необходимо переименовать переменные, используемые в области макетов, с префиксом mock (например, mockUsers).

Jest делает некоторую магию подъема, чтобы позволить вам заменить импортированные модули на mocks, но, похоже, для этого нужны специальные префиксы имен переменных.

...