我正在尝试模拟将数据检索到组件中的fetch()。
I'm using this as a model for mocking my fetches,但我无法使其正常工作。
运行测试时出现此错误:babel-plugin-jest-hoist: The module factory of 'jest.mock()' is not allowed to reference any out-of-scope variables.
有没有办法让这些函数返回模拟数据,而不是实际尝试进行真正的API调用?
以角色映射到每个用户的方式返回用户。
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;
})
);
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 (...)
};
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();
});
答案 0 :(得分:3)
默认情况下,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);
...并将模拟的响应添加到空的模拟函数中。
答案 1 :(得分:0)
您需要重命名模拟范围内使用的变量,并以mock
为前缀(例如mockUsers
)。
Jest做了一些提升魔术,使您可以用模拟替换导入的模块,但是似乎确实需要这些特殊的变量名前缀来完成它的工作。
答案 2 :(得分:0)
这是一个广义答案。首先,在测试内部进行jest.mock(../service)
。
然后转到您的服务文件,在其中进行呼叫。创建一个__mocks__
文件,然后在内部创建一个与您的服务文件相同的文件。
然后为您想要的每个服务创建函数,这些函数可以解析承诺以与常规调用一样异步。
const getUser = () => promise.resolve()
以上是一般示例。如果需要数据,请创建一个您自己选择的模拟数据对象/数组,并使用该函数为其提供服务。
答案 3 :(得分:0)
不要嘲笑进行 API 调用的工具;存根服务器响应。下面是我将如何使用名为 nock 的 HTTP 拦截器重写您的测试。
import "jest-dom/extend-expect";
import React from "react";
import { render, waitFor } from "react-testing-library";
import UserTable from "../UserTable";
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"
}
]
}
];
describe("<UserTable/>", () => {
it("shows users", async () => { // <-- Async to let nock kick over resolved promise
nock(`${server}`)
.get('/users')
.reply(200, {
data: users
})
.get('/usersWithRoles')
.reply(200, {
data: usersWithRoles
})
.get('/roles')
.reply(200, {
data: roles
});
const { queryByText } = render(<UserTable />);
await waitFor(() => expect(queryByText("Billy")).toBeTruthy()); // <-- Is this supposed to be "Benglish"?
});
});
现在您的测试套件不知道您如何获取数据,并且您不必维护复杂的模拟。查看 Testing Components that make API calls 进行更深入的探索。