如何用jest.spyOn仅Mock首次调用,后续使用原方法实现?
问题描述
我想通过jest.spyOn仅对方法的首次调用做Mock实现,第2次到第n次调用则使用方法原本的逻辑。但我在TypeScript中写的代码并未生效:
import handler from '../src/handler'; import * as cRedis from '../src/redis'; jest.spyOn(cRedis, 'rGetAsync'); describe('handle cart quantity validation', () => { test('test 1 blabla', async () => { (cRedis.rGetAsync as jest.Mock).mockImplementationOnce( () => new Promise(resolve => setTimeout(() => { resolve('{}'); }, 1000), ), ); const response = await handler(); }); });
我的handler函数会两次调用cRedis.rGetAsync:
// handler.ts import { rGetAsync } from './redis'; export default async function () { const a = await rGetAsync('a'); const b = await rGetAsync('b'); console.log(a, b); }
目前的问题是两次调用都使用了Mock实现,mockImplementationOnce没有只生效一次。我需要首次调用用Mock,第二次用真实逻辑,该怎么实现?
解决方法
问题出在你没有显式指定Mock调用后的后续逻辑要回到原始方法,且直接通过类型转换操作Mock对象容易出问题。正确的做法是:
- 先保存原始方法的引用,避免Spy替换后无法访问原始逻辑
- 保存
jest.spyOn返回的Spy对象,直接通过它配置Mock行为
完整版兼容代码
import handler from '../src/handler'; import * as cRedis from '../src/redis'; // 提前保存原始方法引用 const originalRGetAsync = cRedis.rGetAsync; describe('handle cart quantity validation', () => { test('test 1 blabla', async () => { // 创建Spy并保存引用 const rGetAsyncSpy = jest.spyOn(cRedis, 'rGetAsync'); // 首次调用使用Mock实现 rGetAsyncSpy.mockImplementationOnce(() => new Promise(resolve => setTimeout(() => resolve('{}'), 1000)) ); // 后续调用强制指向原始方法 rGetAsyncSpy.mockImplementation((...args) => originalRGetAsync(...args)); const response = await handler(); // 此处可添加测试断言 }); // 测试结束后恢复所有Mock,避免污染其他用例 afterEach(() => { jest.restoreAllMocks(); }); });
简化版(适用于Jest 27+)
如果你的Jest版本较新,jest.spyOn默认会保留原始方法实现,仅用mockImplementationOnce指定首次调用的Mock即可,后续调用会自动执行原始逻辑:
import handler from '../src/handler'; import * as cRedis from '../src/redis'; describe('handle cart quantity validation', () => { test('test 1 blabla', async () => { const rGetAsyncSpy = jest.spyOn(cRedis, 'rGetAsync'); // 仅指定首次调用的Mock,后续自动使用原始实现 rGetAsyncSpy.mockImplementationOnce(() => new Promise(resolve => setTimeout(() => resolve('{}'), 1000)) ); const response = await handler(); }); afterEach(() => { jest.restoreAllMocks(); }); });
关键说明
- 保存Spy对象而非直接修改模块导出方法,能避免TypeScript类型转换带来的意外问题
- 显式指定后续调用指向原始方法的写法兼容性更强,适合多版本Jest环境
afterEach中调用jest.restoreAllMocks()可确保测试用例之间相互独立,避免Mock残留影响
内容的提问来源于stack exchange,提问作者Kristi Jorgji
相关产品推荐
相关产品推荐

