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

如何用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对象容易出问题。正确的做法是:

  1. 先保存原始方法的引用,避免Spy替换后无法访问原始逻辑
  2. 保存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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 18:45:34