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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 20:53:11