You need to enable JavaScript to run this app. Jest能否仅mock外部模块指定部分,而非完整mock整个依赖库?
优惠活动
大模型
产品
解决方案
定价
更多

Jest能否仅mock外部模块指定部分,而非完整mock整个依赖库?

Jest 部分 mock 模块的实现方案

完全可以实现只 mock 模块的部分导出,不需要全量替换整个依赖库的实现。结合你的场景,有两种适配度最高的方案,可根据需求选择。

方案一(改动最小,推荐):使用 jest.spyOn 直接替换目标方法

这种方式不需要编写全量的模块 mock 逻辑,只需要针对DatabaseFacade的get方法做替换,其他所有框架导出的内容(DatabaseModule、Pg等)完全保留真实实现,和你现有代码的兼容性最高。

改动代码

只需要在原有测试用例的beforeAll和afterAll里补充两行spy逻辑即可,其余代码完全不用改动:

import { DatabaseModule, Pg, DatabaseFacade } from '@organisation/framework'
import { StationRepository } from './StationRepository'
import { DatabaseConfig } from '../../spec'

const DatabaseInstance = new DatabaseModule()

beforeAll(async () => {
  DatabaseInstance.add('default', new Pg('default', DatabaseConfig))
  // 替换DatabaseFacade的get方法,让它返回你自定义创建的数据库实例
  jest.spyOn(DatabaseFacade, 'get').mockImplementation(() => DatabaseInstance)
  jest.clearAllMocks()
})

afterAll(async () => {
  // 测试完成后恢复原方法,避免影响其他用例
  jest.restoreAllMocks()
  const database = DatabaseInstance.get()
  await database.disconnect()
})

// 你原本的测试用例完全复用,不需要修改
describe('find timezone', () => {
  it('can find a timezone', async () => {
    const ids = [1, 2]
    const expected = ['Pacific/Auckland', 'Australia/Adelaide']
    const results = []
    for (const id of ids) {
      const timezone: string | null = await StationRepository.findTimezone(id)
      results.push(timezone)
    }

    expect(results).toEqual(expected)
  })
})

可选扩展:纯单元测试不连真实数据库

如果不想依赖真实的数据库连接,可以进一步mockqueryOne方法的返回值,完全脱离数据库环境:

beforeAll(async () => {
  DatabaseInstance.add('default', new Pg('default', DatabaseConfig))
  jest.spyOn(DatabaseFacade, 'get').mockImplementation(() => DatabaseInstance)
  // 直接mock查询方法的返回值,不需要真实查库
  jest.spyOn(DatabaseInstance, 'queryOne').mockImplementation(async (_query, params) => {
    const stationId = params[0]
    if (stationId === 1) return { timezone: 'Pacific/Auckland' }
    if (stationId === 2) return { timezone: 'Australia/Adelaide' }
    return null
  })
})

方案二:部分 mock 整个模块

如果你需要更彻底地控制模块的导出内容,可以使用Jest原生的部分mock能力,逻辑是先加载模块的所有真实实现,仅覆盖需要替换的导出项:

// 注意:jest.mock会被Jest自动提升到文件最顶部执行
jest.mock('@organisation/framework', () => {
  // 拿到模块的所有真实导出
  const actualModule = jest.requireActual('@organisation/framework')
  // 自定义要替换的DatabaseFacade实现
  const mockDatabaseFacade = {
    get: jest.fn()
  }
  return {
    // 保留所有原模块的真实导出
    ...actualModule,
    // 仅覆盖需要替换的DatabaseFacade
    DatabaseFacade: mockDatabaseFacade
  }
})

// 正常导入依赖
import { DatabaseModule, Pg, DatabaseFacade } from '@organisation/framework'
import { StationRepository } from './StationRepository'
import { DatabaseConfig } from '../../spec'

const DatabaseInstance = new DatabaseModule()

beforeAll(async () => {
  DatabaseInstance.add('default', new Pg('default', DatabaseConfig))
  // 给mock的get方法绑定返回值
  DatabaseFacade.get.mockImplementation(() => DatabaseInstance)
  jest.clearAllMocks()
})

afterAll(async () => {
  jest.restoreAllMocks()
  const database = DatabaseInstance.get()
  await database.disconnect()
})

这种方式的效果和方案一完全一致,适合需要一次性替换多个模块导出的场景。

注意事项

  • 不要全量mock整个@organisation/framework模块,否则你之前已经跑通的数据库连接创建逻辑会全部失效,这也是你之前遇到的问题的核心原因
  • 所有mock的方法/实例,要在测试结束后通过jest.restoreAllMocks或者mockClear做清理,避免影响其他测试用例的运行
  • 如果是纯单元测试,建议mockqueryOne方法的返回值,不需要连真实数据库,测试速度和稳定性都会高很多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.10 05:54:06