如何正确使用jest.mock模拟simple-git的branchLocal函数?
如何正确模拟simple-git来测试分支工单ID提取函数?
你的问题核心在于jest.mock的执行时机——jest会把所有jest.mock调用提升到模块作用域的最顶部,所以你在it测试块里写的mock定义根本没机会生效,当你的getTicketIdFromBranchName函数导入时,它依赖的还是原始的simple-git模块,而非你在测试里定义的mock版本。
下面给你两种可行的修正方案,覆盖不同的测试场景:
方案一:全局mock + 动态修改返回值(推荐)
把mock定义放在模块顶部,这样所有测试都会使用mock后的simple-git,然后在每个测试用例里动态调整mock方法的返回值:
import { getTicketIdFromBranchName } from '@/utils/git-info'; import simpleGit from 'simple-git'; // 提前mock整个simple-git模块,这行代码会被jest自动提升到模块最顶部 jest.mock('simple-git', () => { // 创建一个带有mock方法的git实例 const mockGitInstance = { branchLocal: jest.fn(), }; // 返回一个工厂函数,模拟simpleGit()调用返回实例的行为 return jest.fn(() => mockGitInstance); }); const TICKET_ID_REGEX = /((?<!([A-Z]{1,10})-?)[A-Z]+-\d+)/.source; describe('[utils/git-info]', () => { it('getTicketIdFromBranchName | 分支名称包含工单ID时应正确返回', async () => { const ticketId = 'CLO-1234'; // 获取mock的git实例,设置branchLocal的成功返回值 const mockGit = simpleGit(); mockGit.branchLocal.mockResolvedValue({ current: `${ticketId} DUMMY TEST` }); const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX)); expect(result).toBe(ticketId); }); it('getTicketIdFromBranchName | 分支名称无工单ID时应返回null', async () => { const mockGit = simpleGit(); mockGit.branchLocal.mockResolvedValue({ current: 'feature/no-ticket-fix' }); const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX)); expect(result).toBeNull(); }); it('getTicketIdFromBranchName | Git命令执行失败时应返回null', async () => { const mockGit = simpleGit(); mockGit.branchLocal.mockRejectedValue(new Error('Failed to fetch branches')); const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX)); expect(result).toBeNull(); }); });
方案二:动态mock(适合每个测试需要独立mock的场景)
如果你的测试用例需要完全独立的mock实现,可以用jest.resetModules清空模块缓存,再在每个测试里重新mock并导入目标函数:
const TICKET_ID_REGEX = /((?<!([A-Z]{1,10})-?)[A-Z]+-\d+)/.source; describe('[utils/git-info]', () => { beforeEach(() => { // 每次测试前清空模块缓存,确保重新加载模块和mock jest.resetModules(); }); it('getTicketIdFromBranchName | 分支名称包含工单ID时应正确返回', async () => { const ticketId = 'CLO-1234'; // 在当前测试里mock simple-git jest.mock('simple-git', () => { const mGit = { branchLocal: jest.fn(() => Promise.resolve({ current: `${ticketId} DUMMY TEST` })), }; return jest.fn(() => mGit); }); // 重新导入目标函数(此时模块已被重置,使用当前测试的mock) const { getTicketIdFromBranchName } = require('@/utils/git-info'); const result = await getTicketIdFromBranchName(new RegExp(TICKET_ID_REGEX)); expect(result).toBe(ticketId); }); });
关键注意点
- jest.mock的提升机制:永远记住
jest.mock会被自动提升到模块最顶部,不要在测试块里定义全局mock,否则会不生效。 - 异步函数的mock:推荐使用
mockResolvedValue/mockRejectedValue来模拟异步函数的返回,比直接写Promise.resolve更直观。 - 工厂函数的mock:
simple-git是一个工厂函数(调用simpleGit()返回实例),所以mock时要模拟这个工厂行为,返回带有mock方法的实例对象。
内容的提问来源于stack exchange,提问作者Tal Rofe
相关产品推荐
相关产品推荐

