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

Jest mock module functions used in another module

我是 Jest 的新手,正在尝试对一些现有代码编写一些测试。虽然我遇到过很多关于模块内模拟函数的 Jest 文章和 Whosebug 帖子(例如 ),但我相信我正在尝试完成一些有点高级或非常愚蠢并且需要的东西代码重构。无论哪种方式,我都不确定,非常感谢您的帮助!

让我用示例代码解释一下我的情况。假设我们正在处理一个电子商务系统,后端有一个模块可以从 API:

中获取订单详细信息
// get-order-data.js

const getOrderDataFromApi = async (orderId) => {
    // . . . some API call
    console.log("The actual getOrderData function called!");
}

// Added only to emphasize that the entire module shouldn't be mocked
const getOrderDataFromDatabase = async (orderId) => { }

module.exports = {
    getOrderDataFromApi,
    getOrderDataFromDatabase,
}

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

// get-delivery-data.js

const getDeliveryDataFromApi = async (orderId) => {
    // . . . some API call
    console.log("The actual getDeliveryDataFromApi function called!");
}

// Added only to emphasize that the entire module shouldn't be mocked
const getDeliveryDataFromDatabase = async (orderId) => { }

module.exports = {
    getDeliveryDataFromApi,
    getDeliveryDataFromDatabase,
}

可以看到,每个模块中都有一个console.log,用来表示执行的是原始代码,而不是mocked代码。

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

// combine-order-data.js
const { getDeliveryDataFromApi } = require("./get-delivery-data");
const { getOrderDataFromApi } = require("./get-order-data")

const combineOrderData = async (orderId) => {
    const orderData = await getOrderDataFromApi(orderId);
    const deliveryData = await getDeliveryDataFromApi(orderId);

    return {
        orderData,
        deliveryData,
    };
}

module.exports = {
    combineOrderData,
};

我想通过模拟 getOrderDataFromApi()getDeliveryDataFromApi().

来测试这个 combineOrderData() 函数的行为

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

// order.test.js

const { combineOrderData } = require("./combine-order-data");

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

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

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

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

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

  console.log
    The actual getOrderData function called!

      at getOrderDataFromApi (get-order-data.js:3:13)

  console.log
    The actual getDeliveryDataFromApi function called!

      at getDeliveryDataFromApi (get-delivery-data.js:3:13)

 PASS  ./order.test.js
  ✓ combineOrderData correctly combines data (28 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.227 s
Ran all test suites related to changed files.

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

共享

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

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

我在测试中发现了 2 个问题

  1. 使用 jest.mock 时,应在回调中返回对象,在您的情况下缺少括号
jest.mock("./get-delivery-data", () => ({
  getDeliveryDataFromApi: jest.fn(() => Promise.resolve(mockedDelieryData)),
}));
  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);
});