局部 mock 的核心是只替换模块中特定导出项而非整个模块,常用 jest.spyon 配合顶层 import 实现;也可通过 __mocks__/ 目录部分覆盖方法,或用 jest.mock 工厂函数内联定义;需注意加载时机、引用一致性及 esm/commonjs 差异。

局部 Mock 的核心是只替换某个模块中特定的导出项,而不是整个模块。它适合测试时只想干预一两个函数、保留其余真实行为的场景,比如 mock 掉 axios.get 但让 axios.post 仍走真实网络(调试用),或只 stub 某个工具方法而其他逻辑照常运行。
用 jest.spyOn 配合顶层 import
这是最常用、也最可控的局部 Mock 方式。前提是被测模块以具名/命名空间方式导出目标函数,并且你能在测试中拿到该模块的引用。
- 在测试文件顶部 先 import 模块,再调用 spyOn(不能颠倒)
- 确保导入方式与模块导出一致:ESM 下推荐
import * as mod from 'xxx',避免默认导入导致引用丢失 - spyOn 返回的是可链式配置的 mock 函数,例如:
jest.spyOn(api, 'fetchUser').mockResolvedValue({ id: 1 }) - 每个
it测试结束后建议清理:afterEach(() => jest.restoreAllMocks())
手动 mock 中只覆盖部分方法
如果你用了 __mocks__/ 手动模拟,也可以只重写需要替换的方法,其余复用真实实现。
- 在
__mocks__/api.js中,先const actual = jest.requireActual('./api') - 再基于
actual创建新对象,只覆盖指定方法:export default { ...actual, fetchUser: jest.fn().mockResolvedValue(...) } - 这样既保持了模块结构一致性,又实现了“局部”替换
内联工厂函数实现精细控制
当需要为不同测试用例提供不同行为,又不想污染全局 mock 时,可以用 jest.mock 的工厂函数形式。
- 在测试顶部写:
jest.mock('lodash', () => ({ debounce: jest.fn(), get: jest.fn().mockReturnValue('mocked') })) - 注意 ESM 环境下需显式加
__esModule: true属性,否则可能报错 - 这种写法不依赖
__mocks__/目录,适合临时、轻量、一次性的局部替换
避开常见陷阱
局部 Mock 失效往往不是语法问题,而是加载时机或引用路径不对。
- 不要在
it或beforeEach里调用jest.mock()——它只在模块加载前生效 - 如果被测模块内部直接
import axios from 'axios',然后调用axios.get(),那你用spyOn(axios, 'get')是无效的,因为模块内部持有的是原始引用;此时应改用jest.mock('axios')或重构为依赖注入 - CommonJS 和 ESM 对模块缓存处理不同,跨测试文件时建议加
jest.resetModules()避免残留











