Vitest如何Mock被测试函数所调用的内部函数?
如何Mock模块内的函数并让实际函数调用Mock版本?
我想Mock hello 函数并使用.mockReturnValue("Hi "),但运行测试后仍返回Hello John,且hello函数未被调用。请问如何在实际的greet函数中Mockhello()的实现?
示例代码:
// greet.ts export function hello() { return "Hello "; } export function greet(name: string) { return hello() + name; }
测试文件:
// greet.spec.ts import {hello, greet} from "./greet"; vi.mock("./greet", async () => { const mod = await vi.importActual<typeof import("./greet")>("./greet"); const hello = vi.fn().mockReturnValue("Hi "); return { ...mod, hello, // greet: vi.fn((name: string)=> hello() + name), // ^取消注释后可用,但违背测试目的 // 难道我想要的效果无法实现? } }) describe("Greeting", () => { test("should return a personalized greeting", async () => { const result = greet("John"); expect(result).toBe("Hi John"); expect(hello).toHaveBeenCalled(); }); });
问题原因
核心问题在于:greet函数内部直接调用的是模块内本地定义的hello函数,而非你通过vi.mock替换的导出版本。ES模块中,内部函数的引用是闭包绑定,不会自动关联到导出的Mock函数。
解决方案
方案1:调整原代码的引用方式(推荐)
修改greet.ts,让greet通过模块自身的导出引用hello,这样Mock的导出函数就能被实际调用:
// greet.ts import * as self from './greet'; export function hello() { return "Hello "; } export function greet(name: string) { // 改为调用导出的hello函数 return self.hello() + name; }
此时你原来的测试代码无需修改,运行测试就能得到Hi John的预期结果,且helloMock函数会被正确调用。
方案2:在Mock中重写greet逻辑(无需修改原代码)
如果无法调整原代码,可以在vi.mock中重新定义greet函数,让它依赖Mock的hello,同时复用原函数的逻辑:
// greet.spec.ts import { hello, greet } from "./greet"; vi.mock("./greet", async () => { const actual = await vi.importActual<typeof import("./greet")>("./greet"); const mockHello = vi.fn().mockReturnValue("Hi "); return { ...actual, hello: mockHello, // 重新实现greet,使用mock的hello greet: (name: string) => mockHello() + name }; }); describe("Greeting", () => { test("should return a personalized greeting", () => { const result = greet("John"); expect(result).toBe("Hi John"); expect(hello).toHaveBeenCalled(); }); });
这种方式无需改动原代码,适合不能修改源代码的场景,虽然需要重写greet的逻辑,但能达到测试目的。
内容的提问来源于stack exchange,提问作者wongx
相关产品推荐
相关产品推荐

