Jest 模拟模块函数在另一个模块中使用

ank*_*981 4 node.js jestjs

我是 Jest 新手,正在尝试对一些现有代码编写一些测试。虽然我遇到过很多关于模块内模拟函数的 Jest 文章和 Stackoverflow 帖子(例如this),但我相信我正在尝试实现一些有点先进或非常愚蠢的东西,并且需要代码重构。不管怎样,我不确定,并且非常感谢一些帮助!

\n

让我使用示例代码来解释我的情况。假设我们正在处理一个电子商务系统,后端有一个模块可以从某些 API 获取订单详细信息:

\n
// get-order-data.js\n\nconst getOrderDataFromApi = async (orderId) => {\n    // . . . some API call\n    console.log("The actual getOrderData function called!");\n}\n\n// Added only to emphasize that the entire module shouldn\'t be mocked\nconst getOrderDataFromDatabase = async (orderId) => { }\n\nmodule.exports = {\n    getOrderDataFromApi,\n    getOrderDataFromDatabase,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

还有一个非常相似的模块,用于获取订单的交付数据:

\n
// get-delivery-data.js\n\nconst getDeliveryDataFromApi = async (orderId) => {\n    // . . . some API call\n    console.log("The actual getDeliveryDataFromApi function called!");\n}\n\n// Added only to emphasize that the entire module shouldn\'t be mocked\nconst getDeliveryDataFromDatabase = async (orderId) => { }\n\nmodule.exports = {\n    getDeliveryDataFromApi,\n    getDeliveryDataFromDatabase,\n}\n
Run Code Online (Sandbox Code Playgroud)\n

可以看到,console.log每个模块中都有一个,用于指示已经执行了原始代码而不是模拟代码。

\n

现在,假设这两个模块都被另一个模块函数使用,如下所示:

\n
// combine-order-data.js\nconst { getDeliveryDataFromApi } = require("./get-delivery-data");\nconst { getOrderDataFromApi } = require("./get-order-data")\n\nconst combineOrderData = async (orderId) => {\n    const orderData = await getOrderDataFromApi(orderId);\n    const deliveryData = await getDeliveryDataFromApi(orderId);\n\n    return {\n        orderData,\n        deliveryData,\n    };\n}\n\nmodule.exports = {\n    combineOrderData,\n};\n
Run Code Online (Sandbox Code Playgroud)\n

combineOrderData()我想通过模拟getOrderDataFromApi()和来测试这个函数的行为getDeliveryDataFromApi()

\n

为此,我创建了以下测试文件:

\n
// order.test.js\n\nconst { combineOrderData } = require("./combine-order-data");\n\nconst mockedOrderData = {\n    id: 1,\n    amount: 1000,\n};\n\nconst mockedDelieryData = {\n    orderId: 1,\n    status: \'DELIVERED\',\n};\n\nbeforeEach(() => {\n    jest.mock("./get-order-data", () => {\n        getOrderDataFromApi: jest.fn(() => Promise.resolve(mockedOrderData))\n    });\n    jest.mock("./get-delivery-data", () => {\n        getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData))\n    });\n});\n\ntest("combineOrderData correctly combines data", async () => {\n    const combinedOrder = await combineOrderData(111);\n});\n
Run Code Online (Sandbox Code Playgroud)\n

当我现在运行时npx jest,我console.log在输出中看到 s,告诉我模拟没有成功:

\n
  console.log\n    The actual getOrderData function called!\n\n      at getOrderDataFromApi (get-order-data.js:3:13)\n\n  console.log\n    The actual getDeliveryDataFromApi function called!\n\n      at getDeliveryDataFromApi (get-delivery-data.js:3:13)\n\n PASS  ./order.test.js\n  \xe2\x9c\x93 combineOrderData correctly combines data (28 ms)\n\nTest Suites: 1 passed, 1 total\nTests:       1 passed, 1 total\nSnapshots:   0 total\nTime:        0.227 s\nRan all test suites related to changed files.\n
Run Code Online (Sandbox Code Playgroud)\n

我认为发生这种情况是因为当combineOrderData()被调用时,它会实例化自己所使用的模块的副本(我认为 Node 模块默认情况下是单例的;例如,只有导入模块的一份副本存在并由所有require模块共享)?

\n

无论如何,我不知道实现这一目标的正确方法是什么。

\n

另外,如果代码结构被破坏并且重构可以解决问题,请随时发表评论,但我不知道如何避免编写和测试进行其他函数调用的代码,所以看来我会遇到这个问题定期出现问题。

\n

小智 5

我发现测试有 2 个问题

  1. 使用 jest.mock 时,应该在回调中返回对象,在您的情况下缺少括号
jest.mock("./get-delivery-data", () => ({
  getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData)),
}));
Run Code Online (Sandbox Code Playgroud)
  1. 模拟导入的顺序不正确。

首先require导入实际模块而不是模拟模块。

工作测试

jest.mock("./get-order-data", () => ({
  getOrderDataFromApi: jest.fn(() => Promise.resolve(mockedOrderData)),
}));
jest.mock("./get-delivery-data", () => ({
  getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData)),
}));
const { combineOrderData } = require("./combine-order-data");

const mockedOrderData = {
  id: 1,
  amount: 1000,
};

const mockedDelieryData = {
  orderId: 1,
  status: "DELIVERED",
};

test("combineOrderData correctly combines data", async () => {
  const combinedOrder = await combineOrderData(111);
});

Run Code Online (Sandbox Code Playgroud)