Jest中mockResolvedValue失效:异步上下文函数返回undefined
问题根源
你的测试报错是因为**main.ts在模块加载阶段就自动执行了registerCommands()**,而此时测试代码里的mockResolvedValue还没来得及设置——Jest的模块级jest.mock只会创建一个空的mock函数(默认返回undefined),it块里的mock配置是在模块加载之后才执行的,所以第一次执行registerCommands()时,loadCommands返回的是undefined,触发了错误。
解决方案
你有两种可行的修复方向:
方向1:修改源码,移除模块顶层的自动调用
把registerCommands()的执行逻辑从模块顶层移出,交给外部调用,这样测试可以完全控制执行时机:
修改main.ts:
import { loadCommands } from "./load-commands"; export async function registerCommands() { const commandRegistry = await loadCommands(); if (!commandRegistry) throw new Error("commandRegistry is undefined"); commandRegistry.forEach((command) => command.register()); } // 移除自动调用,改为导出初始化函数让外部触发 export function initCommands() { registerCommands(); } // 或者在实际业务代码中,由入口文件调用initCommands()
对应的测试代码可以保持原有逻辑,因为现在registerCommands()只会在测试主动调用时执行:
import { registerCommands } from "./main"; import { loadCommands } from "./load-commands"; import { jest } from "@jest/globals"; jest.mock("./load-commands", () => ({ loadCommands: jest.fn(), })); describe("registerCommands", () => { it("should load commands and call register on each", async () => { const mockCommand = { register: jest.fn(), taskName: "Mock Command" }; (loadCommands as jest.Mock).mockResolvedValue([mockCommand]); await registerCommands(); expect(loadCommands).toHaveBeenCalledTimes(1); expect(mockCommand.register).toHaveBeenCalled(); }); });
方向2:不修改源码,用动态导入控制模块加载时机
如果无法修改main.ts的结构,就用Jest的动态导入功能,确保在设置好mock之后再加载main模块:
修改测试代码:
import { jest } from "@jest/globals"; describe("registerCommands", () => { // 每次测试前重置模块,避免缓存影响 beforeEach(() => { jest.resetModules(); }); it("should load commands and call register on each", async () => { const mockCommand = { register: jest.fn(), taskName: "Mock Command" }; // 先设置好完整的mock jest.mock("./load-commands", () => ({ loadCommands: jest.fn().mockResolvedValue([mockCommand]), })); // 动态导入main模块,此时mock已经生效 const { registerCommands } = await import("./main"); await registerCommands(); const { loadCommands } = await import("./load-commands"); expect(loadCommands).toHaveBeenCalledTimes(1); expect(mockCommand.register).toHaveBeenCalled(); }); });
这种方式通过jest.resetModules()清除模块缓存,再用动态导入确保mock配置完成后才加载main.ts,避免了模块顶层自动执行的问题。
内容的提问来源于stack exchange,提问作者I'll-Be-Back
相关产品推荐
相关产品推荐

