如何用Mock函数测试控制台命令?Jest Mock失效问题求助
测试控制台脚本时Jest Mock不生效的解决方法
你遇到的核心问题是:用execSync('ts-node index.ts')启动了独立的Node.js进程,而Jest的Mock仅作用于当前测试进程,因此新进程中的函数不会被Mock。以下是两种无需依赖外部函数副作用的测试方案:
方案一:重构脚本,导出可测试的核心逻辑
将原有的自执行函数改为可导出的函数,同时保留命令行执行的入口,这样测试时可以直接调用函数验证逻辑,无需启动新进程。
修改后的index.ts
import { fn1, fn2, fnErr } from './src/functions'; // 导出核心执行逻辑,方便测试 export const run = () => { try { fn1(); fn2(); } catch { fnErr(); } }; // 仅当脚本作为主模块执行时(命令行启动),才调用run函数 if (require.main === module) { run(); }
对应的测试代码index.test.ts
import { run } from './index'; import { fn1, fn2, fnErr } from './src/functions'; // Mock functions模块 jest.mock('./src/functions', () => ({ fn1: jest.fn(), fn2: jest.fn(), fnErr: jest.fn(), })); describe('Script execution logic', () => { // 每次测试后清除Mock调用记录 afterEach(() => { jest.clearAllMocks(); }); it('正常执行时调用fn1和fn2,不调用fnErr', () => { run(); expect(fn1).toHaveBeenCalledTimes(1); expect(fn2).toHaveBeenCalledTimes(1); expect(fnErr).not.toHaveBeenCalled(); }); it('发生错误时调用fnErr', () => { // 让fn1抛出错误,触发catch分支 (fn1 as jest.Mock).mockImplementation(() => { throw new Error('测试错误'); }); run(); expect(fnErr).toHaveBeenCalledTimes(1); }); });
这种方案直接验证函数调用行为,完全脱离控制台输出等副作用,是最推荐的测试方式。
方案二:模拟命令行启动场景(无需新进程)
如果必须验证脚本作为CLI命令执行的行为,可以在测试进程中模拟require.main === module的场景,直接加载脚本触发执行,Mock可以正常生效。
测试代码index.test.ts
import { fn1, fn2, fnErr } from './src/functions'; // Mock functions模块 jest.mock('./src/functions', () => ({ fn1: jest.fn(), fn2: jest.fn(), fnErr: jest.fn(), })); describe('Command line entry', () => { afterEach(() => { jest.clearAllMocks(); // 清除模块缓存,避免多次导入导致的重复执行 delete require.cache[require.resolve('./index')]; }); it('作为CLI启动时正确执行逻辑', () => { // 保存原有的require.main const originalMain = require.main; // 模拟当前模块是主模块(命令行启动) require.main = module; // 加载脚本,触发执行 require('./index'); // 恢复原有的require.main require.main = originalMain; expect(fn1).toHaveBeenCalled(); expect(fn2).toHaveBeenCalled(); expect(fnErr).not.toHaveBeenCalled(); }); });
这种方式无需启动新进程,Mock可以正常作用,同时也不依赖控制台输出的副作用。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

