mock类方法的关键是替换函数引用而非修改类本身:1. 直接spyon实例方法;2. mock构造函数拦截new行为;3. 替换原型方法(需恢复);4. 依赖注入(推荐)。

对类方法内部实现做 Mock,关键不是“改类本身”,而是控制被测代码实际调用的是哪个函数引用。JS 中类方法默认是绑定在原型或实例上的普通函数,只要能替换掉这个引用,就能让调用走模拟逻辑。
直接 spyOn 实例方法(适用于已创建的实例)
当测试中你拥有目标类的实例对象时,最直接的方式是用 jest.spyOn 拦截该实例上的方法:
- 先 new 出实例,再对其方法打桩:
const instance = new UserService(); jest.spyOn(instance, 'getUserById').mockReturnValue({ id: 1, name: 'Mocked' }); - 这样后续调用
instance.getUserById(123)就不会执行原始逻辑,而是返回预设值 - 注意:必须在实例创建之后、方法被调用之前设置 spy,且不能对箭头函数或私有字段方法(#method)生效
Mock 类的整个构造函数(适用于 new 调用场景)
如果被测代码内部会自己 new UserService(),你就需要拦截构造行为,让 new 出来的对象自带 mock 方法:
- 用
jest.mock('./UserService')+ 工厂函数返回一个带 mock 方法的类 - 或者更轻量地:在测试顶部写
jest.mock('./UserService', () => { return jest.fn().mockImplementation(() => ({ getUserById: jest.fn().mockResolvedValue({ id: 1 }) })); }); - 这样每次 new UserService() 都得到一个“空壳对象”,其方法已被提前定义为 mock 函数
替换原型方法(影响所有实例,慎用)
若想让所有 future 实例都走 mock 行为,可临时修改类的原型链:
UserService.prototype.getUserById = jest.fn().mockResolvedValue({ id: 1 });- 之后无论 new 多少次,调用的都是这个 mock 版本
- 务必在
afterEach中恢复:UserService.prototype.getUserById = originalMethod;,否则污染其他测试
依赖注入 + 构造参数传入(推荐长期维护项目)
真正解耦的做法,是让类不自己创建依赖,而是通过构造函数接收可替换的行为:
- 重构类:把数据获取逻辑抽成函数参数,如
constructor(fetcher = defaultFetcher) { this.fetcher = fetcher; } - 测试时直接传入
jest.fn().mockResolvedValue(...),无需动类定义或原型 - 这种方式不依赖 Jest API,兼容性好,也便于未来切换真实实现(如从 fetch 切到 axios)











