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

Pinia测试疑问:如何验证内部方法被调用的次数?

解决Pinia内部action调用无法被Vitest spy统计的问题

问题原因

你遇到的核心问题是:Pinia内部action之间的调用,使用的是store定义时的原始函数引用,而非你通过vi.spyOn mock的实例方法。

当你用vi.spyOn(store, 'increaseBy10')创建 spy 时,你只替换了 store 实例上的increaseBy10方法,但increaseBy11内部调用的this.increaseBy10,指向的是 store 定义阶段actions对象里的原始函数,所以这次内部调用不会被你的 spy 捕获到,导致调用次数统计错误。

解决方案(无需修改Store代码)

你可以直接 spy on store 的$options.actions中的目标方法,确保内部调用也能被统计到。以下是完整的测试示例:

假设你的 Store 代码如下:

// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    },
    increaseBy10() {
      this.count += 10
    },
    increaseBy11() {
      this.increment()
      this.increaseBy10()
    }
  }
})

对应的测试代码:

import { useCounterStore } from '@/stores/counter'
import { createPinia } from 'pinia'
import { vi, expect, test } from 'vitest'

test('increaseBy11 应正确调用 increaseBy10', () => {
  const pinia = createPinia()
  const store = useCounterStore(pinia)

  // 保存原始方法,避免mock后丢失原有逻辑
  const originalIncreaseBy10 = store.$options.actions.increaseBy10
  // spy on $options.actions里的方法,确保内部调用也能被捕获
  const increaseBy10Spy = vi.spyOn(store.$options.actions, 'increaseBy10').mockImplementation(function(...args) {
    // 绑定store实例上下文,执行原始方法逻辑
    return originalIncreaseBy10.call(this, ...args)
  })

  // 执行测试步骤
  store.increaseBy10() // 外部直接调用,计数+1
  store.increaseBy11() // 内部调用increaseBy10,计数+1

  // 验证调用次数
  expect(increaseBy10Spy).toHaveBeenCalledTimes(2)
  // 同时验证状态更新正确
  expect(store.count).toBe(21)
})

另一种简化写法

如果你不需要自定义mock逻辑,只是想统计调用次数,可以直接用vi.spyOn并保留原实现(Vitest的spyOn默认会调用原方法,不过需要确保上下文正确):

test('increaseBy11 应正确调用 increaseBy10', () => {
  const pinia = createPinia()
  const store = useCounterStore(pinia)

  const increaseBy10Spy = vi.spyOn(store.$options.actions, 'increaseBy10')

  store.increaseBy10()
  store.increaseBy11()

  expect(increaseBy10Spy).toHaveBeenCalledTimes(2)
  expect(store.count).toBe(21)
})

补充说明

  • Pinia 的$options.actions存储了 store 定义时的原始 action 函数,所有内部方法调用最终都会指向这里的引用。
  • 这种方式无需修改你的 Store 实现代码,完全满足你的需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:12:44