如何用Jest Mock graphql-request调用?解决mockResolvedValue报错
Jest Mock graphql-request 报错:TypeError: request.mockResolvedValue is not a function 解决方法
问题场景
编写调用graphql-request库的Jest单元测试时触发上述错误,测试代码如下:
import { jest } from '@jest/globals'; import { request } from 'graphql-request'; import { logActivityPhoneCallScheduled, } from './candidateService'; jest.mock('graphql-request', () => { return { request: jest.fn(), }; }); beforeEach(() => { jest.clearAllMocks(); }); test('scheduled', async () => { request.mockResolvedValue({ addCandidateActivity: { id: 835 } }); const now = Date.now(); const input = { candidateId: 'TBX-001', eventId: 305, userId: 'y29d13lkcas91', userIsStaff: true, scheduledFor: new Date(now + 14 * 24 * 60 * 60 * 1e3).toISOString(), createdAt: new Date(now - 905 * 1e3).toISOString(), }; const expectedActivity = { candidateId: 'TBX-001', type: 'SCHEDULE', action: 'scheduled', newValue: '305', additionalPayload: { type: 'phonecall', scheduled_at: input.scheduledFor, }, userId: 'y29d13lkcas91', userIsStaff: true, createdAt: input.createdAt, }; const result = await logActivityPhoneCallScheduled(input); expect(result).toBe(835); expect(request).toHaveBeenCalledTimes(1); expect(request.mock.calls[0][0]).toBe('http://candidate-service:5000'); expect(request.mock.calls[0][1]).toContain('addCandidateActivity'); expect(request.mock.calls[0][2]).toEqual({ activity: expectedActivity }); });
运行测试时的错误信息:
TypeError: request.mockResolvedValue is not a function 18 | 19 | test('scheduled', async () => { > 20 | request.mockResolvedValue({ addCandidateActivity: { id: 835 } }); | ^ 21 | 22 | const now = Date.now(); 23 | const input = {
报错原因
问题出在jest.mock的实现逻辑上:
当你用jest.mock('graphql-request', () => ({ request: jest.fn() }))模拟模块时,由于ES模块的导出机制,导入的request并非直接是Jest的mock函数实例,而是被Jest包装后的模块导出属性,导致无法直接调用mockResolvedValue这类mock专属方法。
解决方法
方法一:修正jest.mock的返回结构
确保导出的request是Jest mock函数的直接实例,修改后的代码如下:
import { jest } from '@jest/globals'; import { request } from 'graphql-request'; import { logActivityPhoneCallScheduled } from './candidateService'; // 直接返回jest.fn()的实例,确保导入的request是可操作的mock函数 jest.mock('graphql-request', () => ({ request: jest.fn(), })); beforeEach(() => { jest.clearAllMocks(); }); test('scheduled', async () => { // 现在可以正常调用mockResolvedValue request.mockResolvedValue({ addCandidateActivity: { id: 835 } }); const now = Date.now(); const input = { candidateId: 'TBX-001', eventId: 305, userId: 'y29d13lkcas91', userIsStaff: true, scheduledFor: new Date(now + 14 * 24 * 60 * 60 * 1e3).toISOString(), createdAt: new Date(now - 905 * 1e3).toISOString(), }; const expectedActivity = { candidateId: 'TBX-001', type: 'SCHEDULE', action: 'scheduled', newValue: '305', additionalPayload: { type: 'phonecall', scheduled_at: input.scheduledFor, }, userId: 'y29d13lkcas91', userIsStaff: true, createdAt: input.createdAt, }; const result = await logActivityPhoneCallScheduled(input); expect(result).toBe(835); expect(request).toHaveBeenCalledTimes(1); expect(request.mock.calls[0][0]).toBe('http://candidate-service:5000'); expect(request.mock.calls[0][1]).toContain('addCandidateActivity'); expect(request.mock.calls[0][2]).toEqual({ activity: expectedActivity }); });
方法二:使用jest.spyOn替代全模块mock
如果只需要mockrequest单个方法,用spyOn更灵活,无需提前mock整个模块:
import { jest } from '@jest/globals'; import * as graphqlRequest from 'graphql-request'; import { logActivityPhoneCallScheduled } from './candidateService'; beforeEach(() => { jest.clearAllMocks(); }); test('scheduled', async () => { // 创建mock并设置返回值 const mockRequest = jest.spyOn(graphqlRequest, 'request') .mockResolvedValue({ addCandidateActivity: { id: 835 } }); const now = Date.now(); const input = { candidateId: 'TBX-001', eventId: 305, userId: 'y29d13lkcas91', userIsStaff: true, scheduledFor: new Date(now + 14 * 24 * 60 * 60 * 1e3).toISOString(), createdAt: new Date(now - 905 * 1e3).toISOString(), }; const expectedActivity = { candidateId: 'TBX-001', type: 'SCHEDULE', action: 'scheduled', newValue: '305', additionalPayload: { type: 'phonecall', scheduled_at: input.scheduledFor, }, userId: 'y29d13lkcas91', userIsStaff: true, createdAt: input.createdAt, }; const result = await logActivityPhoneCallScheduled(input); expect(result).toBe(835); expect(mockRequest).toHaveBeenCalledTimes(1); expect(mockRequest.mock.calls[0][0]).toBe('http://candidate-service:5000'); expect(mockRequest.mock.calls[0][1]).toContain('addCandidateActivity'); expect(mockRequest.mock.calls[0][2]).toEqual({ activity: expectedActivity }); // 可选:测试结束后恢复原方法 mockRequest.mockRestore(); });
关键说明
- 方法一中,核心是确保
jest.mock返回的request是jest.fn()的直接实例,这样导入的request就能正常调用所有mock方法。 - 方法二中,
spyOn直接针对模块的命名导出方法创建mock,更适合局部mock场景,且可以随时恢复原方法。
内容的提问来源于stack exchange,提问作者Jakub
相关产品推荐
相关产品推荐

