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

在Jest中模拟TypeScript命名导出的单例类

解决Jest模拟GraphQL代理单例方法失效的问题

我完全懂你现在的挫败感——花了好几个小时调试,结果测试还是跑了真实的请求逻辑。问题出在两个核心点上:mock的时机不对,以及模拟的结构没匹配上单例导出的实际情况。

问题根源拆解

  1. jest.mock执行时机太晚:你把jest.mock放在了beforeAll里,但测试文件顶部的import graphql from './graphql'已经提前加载了被测试模块,而这个模块早已经导入了真实的graphQlProxyService实例。此时再去mock模块,根本替换不掉已经存在的真实实例。

  2. 模拟结构不匹配:你当前的mock试图让graphQlProxyService变成一个构造函数(jest.fn().mockImplementation(...)),但实际上你导出的是一个已经实例化完成的对象,不是类本身。这种模拟方式和真实导出的结构完全对不上,自然不会生效。

可行解决方案

方案1:提前mock整个模块(推荐)

把jest.mock移到测试文件最顶部,在导入任何依赖之前执行,这样mock会直接覆盖模块的导出。然后在测试用例里自定义request方法的行为:

import { createMocks } from 'node-mocks-http'
// 先mock模块,再导入被测试代码
jest.mock('@services/graphql-proxy.service', () => ({
  graphQlProxyService: {
    request: jest.fn() // 先创建空的mock函数占位
  }
}))

import graphql from './graphql'
import { graphQlProxyService } from '@services/graphql-proxy.service'

describe('when there is an error with the request', () => {
  it('returns status code 500', async () => {
    // 给mock的request方法指定错误返回(注意异步方法要用mockRejectedValue)
    (graphQlProxyService.request as jest.Mock).mockRejectedValue(new Error('Mock request error'))

    const { req, res } = createMocks({
      method: 'POST',
      body: {},
    })

    await graphql(req, res)
    expect(res._getStatusCode()).toBe(500)
  })
})

方案2:用jest.spyOn监听实例方法

如果你不想提前mock整个模块,也可以直接用jest.spyOn替换单例实例上的request方法:

import { createMocks } from 'node-mocks-http'
import graphql from './graphql'
import { graphQlProxyService } from '@services/graphql-proxy.service'

describe('when there is an error with the request', () => {
  let requestSpy: jest.SpyInstance

  beforeEach(() => {
    // 监听request方法,替换为mock实现
    requestSpy = jest.spyOn(graphQlProxyService, 'request')
      .mockRejectedValue(new Error('Mock request error'))
  })

  afterEach(() => {
    // 测试后恢复原方法,避免影响其他测试用例
    requestSpy.mockRestore()
  })

  it('returns status code 500', async () => {
    const { req, res } = createMocks({
      method: 'POST',
      body: {},
    })

    await graphql(req, res)
    expect(res._getStatusCode()).toBe(500)
  })
})

关键细节提醒

  • 因为request是异步方法,必须用mockRejectedValue模拟错误,不能直接throw new Error()——前者返回rejected的Promise,和真实异步方法抛错的行为一致;后者是同步抛错,会导致测试逻辑和真实代码不符。
  • 记住Jest模块mock的核心规则:jest.mock必须放在所有导入语句之前,才能正确覆盖模块导出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 20:02:37