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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 07:05:55