TypeScript Node.js项目中Vitest 0.33.0模拟副作用函数失败求助
解决Vitest中同模块函数模拟失效的问题
问题原因
你的代码里parent函数直接调用的是模块内部的child函数,而非导出的child引用。Vitest的spyOn或模块mock都是针对导出对象进行拦截,无法替换模块内部直接调用的函数引用,导致实际的child依然被执行。
解决方法
方法1:修改原代码,让parent调用导出的child
调整dummy.ts,让parent通过模块导出对象调用child,这样Vitest就能拦截到这个引用:
// dummy.ts // 引入自身模块,获取导出对象 import * as self from './dummy'; export function parent(): string { // 调用导出的child,而非本地函数 return `foo${self.child()}`; } export function child(): string { console.log('calling actual child'); return 'bar'; }
之后你的测试代码就能正常工作:
// dummy.test.ts import * as dummy from './dummy'; describe('parent', () => { it('should return foobaz', () => { const spy = vi.spyOn(dummy, 'child').mockReturnValueOnce('baz'); expect(dummy.parent()).toBe('foobaz'); expect(spy).toHaveBeenCalled(); // 测试后恢复原函数 spy.mockRestore(); }); });
方法2:不修改原代码,通过模块mock重定义parent
如果不能改动业务代码,可以在mock模块时,重新定义parent函数,让它调用mock后的child:
// dummy.test.ts import * as dummy from './dummy'; vi.mock('./dummy.ts', async () => { const actual = await vi.importActual<typeof import('./dummy.ts')>('./dummy.ts'); // 定义mock的child const mockChild = vi.fn(() => 'baz'); return { ...actual, child: mockChild, // 重写parent,让它调用mock的child parent: () => `foo${mockChild()}` }; }); describe('parent', () => { it('should return foobaz', () => { expect(dummy.parent()).toBe('foobaz'); expect(dummy.child).toHaveBeenCalled(); }); });
方法3:拆分模块(推荐长期方案)
将child函数拆分到单独的模块中,比如utils.ts,从根源避免同模块函数调用的问题:
// utils.ts export function child(): string { console.log('calling actual child'); return 'bar'; } // dummy.ts import { child } from './utils'; export function parent(): string { return `foo${child()}`; }
测试代码:
// dummy.test.ts import * as dummy from './dummy'; import * as utils from './utils'; describe('parent', () => { it('should return foobaz', () => { const spy = vi.spyOn(utils, 'child').mockReturnValueOnce('baz'); expect(dummy.parent()).toBe('foobaz'); expect(spy).toHaveBeenCalled(); spy.mockRestore(); }); });
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

