Jest模拟函数未被调用排查:AWS Lambda单元测试异常
Jest Mock AWS DynamoStore模块不生效问题解决
问题概述
在为AWS Node.js Lambda配置Jest测试时,尝试Mock被多个Lambda共享的dynamoStore模块,但jest.fn的自定义实现始终未被调用。调试发现Lambda代码中的getVehicleMetaKeysFromDeviceId()是Jest Mock对象,但调用时未使用自定义Mock逻辑,导致测试中vehicle_ids为undefined。
相关代码示例
dynamoStore/vehicleMetaConstraints.js
exports.getVehicleMetaKeysFromDeviceId= async (data) => { return data };
dynamoStore/index.js
exports.vehicleMetaConstraints = require("./vehicleMetaConstraints"); // 其他导出...
lambda1/index.js
const { vehicleMetaStore } = require("dynamo-store"); exports.handler = async (event, context, callback) => { const message = event; let vehicle_ids = await vehicleMetaStore.getVehicleMetaKeysFromDeviceId(message.id); // 测试时vehicle_ids为undefined }
lambda1/index.test.js
const { vehicleMetaStore } = require("dynamo-store"); jest.mock("dynamo-store", () => { return { vehicleMetaStore: { getVehicleMetaKeysFromDeviceId: jest.fn(), }, }; }); describe("VehicleStorageLambda", () => { beforeEach(() => { jest.resetModules(); process.env = { ...env }; }); afterEach(() => { jest.clearAllMocks(); }); test("Handles first time publish with existing device", async () => { let functionHandler = require("./index"); vehicleMetaStore.getVehicleMetaKeysFromDeviceId.mockImplementationOnce(() => // 此逻辑从未被调用 Promise.resolve({ device_id: "333936303238510e00210022", }) ); await functionHandler.handler({}); }); });
问题根源与修复方案
1. 模块导出与导入命名不匹配
dynamoStore/index.js导出的是vehicleMetaConstraints,但Lambda代码和测试代码中都尝试导入vehicleMetaStore,这是核心错误之一。
修正方式:
- 调整Lambda代码的导入语句,匹配实际导出名称:
// lambda1/index.js const { vehicleMetaConstraints: vehicleMetaStore } = require("dynamo-store"); - 或修改
dynamoStore/index.js的导出名称:// dynamoStore/index.js exports.vehicleMetaStore = require("./vehicleMetaConstraints");
2. jest.resetModules()导致Mock被重置
测试中beforeEach里的jest.resetModules()会重置所有已加载的模块,包括之前通过jest.mock创建的Mock模块。后续在test中require("./index")时,会重新加载未被Mock的原始模块,导致自定义Mock逻辑失效。
修正方式:
方案A:移除jest.resetModules()
修改测试文件的beforeEach:
beforeEach(() => { process.env = { ...env }; });
方案B:重置后重新Mock(若必须保留jest.resetModules())
调整测试流程,在重置模块后重新执行Mock,并在Mock完成后再加载Lambda handler:
describe("VehicleStorageLambda", () => { let vehicleMetaStore; let functionHandler; beforeEach(() => { jest.resetModules(); process.env = { ...env }; // 重新Mock模块 jest.mock("dynamo-store", () => { return { vehicleMetaStore: { getVehicleMetaKeysFromDeviceId: jest.fn(), }, }; }); // 加载Mock后的模块和Lambda handler ({ vehicleMetaStore } = require("dynamo-store")); functionHandler = require("./index"); }); afterEach(() => { jest.clearAllMocks(); }); test("Handles first time publish with existing device", async () => { vehicleMetaStore.getVehicleMetaKeysFromDeviceId.mockImplementationOnce(() => Promise.resolve({ device_id: "333936303238510e00210022", }) ); await functionHandler.handler({ id: "test-id" }); // 添加断言验证Mock被调用 expect(vehicleMetaStore.getVehicleMetaKeysFromDeviceId).toHaveBeenCalledWith("test-id"); }); });
3. 确保Mock时机正确
必须在加载Lambda handler之前完成Mock配置,否则handler会加载原始模块而非Mock版本。调整测试中require("./index")的时机,确保在mockImplementationOnce之后或者Mock已初始化完成后再加载handler。
内容的提问来源于stack exchange,提问作者Chris Chevalier
相关产品推荐
相关产品推荐

