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

使用jest.spyOn模拟函数失效:原函数仍被调用的问题排查

解决Jest模拟模块内部函数失效的问题

问题核心

你遇到的问题本质是:getValidUserIdList内部直接调用了本地定义的getUserId函数,而非模块导出的版本。不管用jest.spyOn监听导出的函数,还是用jest.mock替换导出内容,实际执行的始终是原本地函数,导致模拟完全不生效。

解决方案

方案1:修改业务代码,让内部调用导出的函数

把getValidUserIdList里的getUserId调用改为引用模块自身的导出,这样Jest的模拟才能作用到内部调用上。

修改后的业务代码:

import * as followers from './followers'; // 导入自身模块

export async function getValidUserIdList(ig, numberOfIdsToGet = 5): Promise<string[]> {
  const userIds = [];
  let attempts = 0;
  while (userIds.length < numberOfIdsToGet && attempts < 50) {
    // 调用导出的getUserId,而非本地函数
    const { userId, valid } = await followers.getUserId(ig);
    if (valid) userIds.push(userId);
    attempts++;
  }

  return userIds;
}

export async function getUserId(ig): Promise<{ userId: string; valid: boolean }> {
  // 原函数逻辑保持不变
  const userIds = await getRandomPublicProfiles(1);
  const userId = userIds[0];
  const userInfoResponse = await ig.http.full({
    url: `api/v1/users/${userId}/info/`,
    throwHttpErrors: false,
  });

  let isUserIdValid = true;
  if (userInfoResponse.statusCode === 400) isUserIdValid = false;
  if (userInfoResponse.statusCode === 200 && userInfoResponse.body?.user?.is_private) isUserIdValid = false;

  return { userId, valid: isUserIdValid };
}

此时你的jest.spyOn测试就能正常工作:

describe('getValidUserIdList', () => {
  it('returns correct number of valid user IDs', async () => {
    const mockResponse = { statusCode: 200, body: { user: { is_private: false } } };
    const ig = jest.fn();
    ig['http'] = { full: jest.fn(() => mockResponse) };

    const getUserIdSpy = jest.spyOn(followers, 'getUserId').mockResolvedValue({ userId: '123', valid: true });
    await followers.getValidUserIdList(ig);
    expect(getUserIdSpy).toHaveBeenCalledTimes(5);
  });
});

方案2:用Jest的jest.mock完全模拟(无需修改业务代码)

如果不想改动业务代码,可以通过jest.mock替换模块导出,同时保留其他函数的原实现:

测试文件顶部添加:

jest.mock('./followers', () => {
  const original = jest.requireActual('./followers');
  return {
    __esModule: true,
    ...original,
    getUserId: jest.fn().mockResolvedValue({ userId: '123', valid: true }),
  };
});

// 必须在jest.mock之后导入模块
import * as followers from './followers';

describe('getValidUserIdList', () => {
  it('returns correct number of valid user IDs', async () => {
    const ig = {}; // 无需模拟ig.http,因为getUserId已被完全mock
    await followers.getValidUserIdList(ig);
    expect(followers.getUserId).toHaveBeenCalledTimes(5);
  });
});

这种方式的前提是:业务代码最终会调用到模块导出的getUserId版本,而非完全隔离的本地函数。

原有尝试失效的原因

  • jest.spyOn:你监听的是模块导出的getUserId,但getValidUserIdList内部调用的是未导出的本地函数,两者不是同一个引用,模拟无法作用到内部执行。
  • jest.mock:同样因为业务代码内部调用的是本地函数,替换导出的函数不会影响内部逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 05:03:28