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
相关产品推荐
相关产品推荐

