使用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
相关产品推荐
相关产品推荐

