You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何正确使用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);
  });
});

关键注意点

  1. jest.mock的提升机制:永远记住jest.mock会被自动提升到模块最顶部,不要在测试块里定义全局mock,否则会不生效。
  2. 异步函数的mock:推荐使用mockResolvedValue/mockRejectedValue来模拟异步函数的返回,比直接写Promise.resolve更直观。
  3. 工厂函数的mock:simple-git是一个工厂函数(调用simpleGit()返回实例),所以mock时要模拟这个工厂行为,返回带有mock方法的实例对象。

内容的提问来源于stack exchange,提问作者Tal Rofe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 17:52:45