使用Vitest和Pinia时无法监视嵌套函数调用的问题排查
问题
我定义了一个简单的Pinia store:
export const useFormsContextStore = defineStore('formsContext', () => { function makeSomeCalculations() { // calculations // and fire helper function helper(); } function helper() { console.log('helper function called from pinia store'); } return { calculate: makeSomeCalculations, calculateHelper: helper, helper, }; });
我希望测试执行calculate()时helper()方法是否被调用,因此编写了如下单元测试:
describe('Forms Context store tests', () => { let pinia: Pinia | undefined = undefined; beforeEach(() => { pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false, }); setActivePinia(pinia); }); afterEach(() => { vi.resetAllMocks(); }); it('unit test', async () => { const store = useFormsContextStore(pinia); store.calculate(); expect.soft(store.calculate).toHaveBeenCalledOnce(); expect.soft(store.helper).toHaveBeenCalledOnce(); }); });
但最后一个断言抛出异常:
expected spy to be called once but got 0 times
原因分析
问题出在store内部直接调用的是函数作用域内的原始helper函数,而非返回给store实例的被Spy包装后的helper方法。
当你通过createTestingPinia创建测试Pinia时,它会对store返回的方法进行Spy包装,赋值给store实例(也就是store.helper)。但makeSomeCalculations里调用的是定义时的原始helper函数,这个函数并没有被Spy监控,所以测试时断言store.helper的调用次数自然为0。
解决方案
方法一:修改store内部调用方式,使用store实例的helper
把store的定义改成普通函数(不能用箭头函数,否则this指向错误),在makeSomeCalculations里通过this调用helper:
export const useFormsContextStore = defineStore('formsContext', function() { function makeSomeCalculations() { // calculations this.helper(); } function helper() { console.log('helper function called from pinia store'); } return { calculate: makeSomeCalculations, calculateHelper: helper, helper, }; });
方法二:直接Mock原始helper函数
如果不想修改store逻辑,可以把helper单独导出,在测试中Mock它:
修改store:
export const helper = () => { console.log('helper function called from pinia store'); }; export const useFormsContextStore = defineStore('formsContext', () => { function makeSomeCalculations() { // calculations helper(); } return { calculate: makeSomeCalculations, calculateHelper: helper, helper, }; });
测试代码调整:
import { useFormsContextStore, helper } from '@/stores/formsContext'; describe('Forms Context store tests', () => { let pinia: Pinia | undefined = undefined; beforeEach(() => { vi.mocked(helper).mockClear(); pinia = createTestingPinia({ createSpy: vi.fn, stubActions: false, }); setActivePinia(pinia); }); it('unit test', async () => { const store = useFormsContextStore(pinia); store.calculate(); expect.soft(store.calculate).toHaveBeenCalledOnce(); expect.soft(helper).toHaveBeenCalledOnce(); }); });
内容的提问来源于stack exchange,提问作者lkurylo
相关产品推荐
相关产品推荐

