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

如何在Vitest中仅对单个测试Mock模块函数,其余用实际实现

在Vitest中混合使用真实实现与Mock测试工具函数

场景代码

我有两个工具函数:callMethodIf 会调用并返回 blackbox 函数的结果:

~~/utils/call-method-if.js

import { blackbox } from '~~/utils/blackbox';

export const callMethodIf = (condition) => {
    return blackbox(condition);
};

~~/utils/blackbox.js

export const blackbox = (condition) => {
    return { called: condition };
};

需求

我需要实现两个测试用例:

  • 一个测试调用 blackbox 的真实实现
  • 另一个测试使用 Mock 后的 blackbox 返回值

尝试过的写法及问题

写法1:测试块内Mock

单独运行每个测试都正常,但组合运行时失败,调用 vi.clearAllMocks() 或 vi.resetAllMocks() 也无法解决:

import { describe, expect, it } from 'vitest';

import { callMethodIf } from '~~/utils/call-method-if';

describe('Call method if', () => {
    it('returns "called: true" if condition is true', () => {
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', () => {
        vi.mock('~~/utils/blackbox', () => ({
            blackbox: vi.fn().mockReturnValue({ mock: true })
        }));
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

写法2:全局Mock后尝试覆盖

全局定义Mock后,在第一个测试中尝试覆盖为真实实现,同样无效:

import { describe, expect, it } from 'vitest';

import { callMethodIf } from '~~/utils/call-method-if';

vi.mock('~~/utils/blackbox', () => ({
    blackbox: vi.fn().mockReturnValue({ mock: true })
}));

describe('Call method if', () => {
    it('returns "called: true" if condition is true', () => {
        vi.mock('~~/utils/blackbox', async () => ({
            blackbox: (await vi.importActual('~~/utils/blackbox')).blackbox
        }));
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', () => {
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

正确解决方案

问题出在 vi.mock 是静态执行的,会在模块加载阶段就生效,测试块内的 vi.mock 不会改变已加载的模块。需要结合 vi.resetModules() 和动态导入,或者使用 vi.doMock 来动态控制Mock:

方案1:每个测试用例重置模块并动态导入

import { describe, expect, it, vi } from 'vitest';

describe('Call method if', () => {
    // 每个测试前重置模块,避免相互影响
    beforeEach(() => {
        vi.resetModules();
    });

    it('returns "called: true" if condition is true', async () => {
        // 直接导入真实模块
        const { callMethodIf } = await import('~~/utils/call-method-if');
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', async () => {
        // 先Mock模块,再导入
        vi.doMock('~~/utils/blackbox', () => ({
            blackbox: vi.fn().mockReturnValue({ mock: true })
        }));
        const { callMethodIf } = await import('~~/utils/call-method-if');
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
        // 可选:验证Mock被调用
        const { blackbox } = await import('~~/utils/blackbox');
        expect(blackbox).toHaveBeenCalledWith(false);
    });
});

方案2:使用 vi.importActual 切换真实实现

如果希望全局Mock,在特定测试中切换回真实实现,可以这样写:

import { describe, expect, it, vi } from 'vitest';

// 全局Mock blackbox
vi.mock('~~/utils/blackbox', () => ({
    blackbox: vi.fn().mockReturnValue({ mock: true })
}));

describe('Call method if', () => {
    it('returns "called: true" if condition is true', async () => {
        // 获取真实的blackbox实现
        const { blackbox: actualBlackbox } = await vi.importActual('~~/utils/blackbox');
        // 导入并替换Mock的实现
        const { blackbox } = await import('~~/utils/blackbox');
        blackbox.mockImplementation(actualBlackbox);

        const { callMethodIf } = await import('~~/utils/call-method-if');
        const result = callMethodIf(true);
        expect(result).toEqual({ called: true });
    });

    it('returns mocked blackbox return object', async () => {
        const { callMethodIf } = await import('~~/utils/call-method-if');
        const result = callMethodIf(false);
        expect(result).toEqual({ mock: true });
    });
});

关键说明

  • vi.resetModules():在每个测试前重置已加载的模块,确保每次导入都是全新的实例
  • vi.doMock():动态Mock模块,会影响后续的模块导入
  • vi.importActual():获取模块的真实实现,用于临时覆盖Mock

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 05:30:44