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

使用Jest测试嵌套Pinia Store Action遇类型错误求助

测试嵌套Pinia Store Action时的Jest Mock问题

在使用Jest测试嵌套Pinia Store的Action时遇到类型错误:

TypeError: foodStore.getSnack.mockReturnValue is not a function

取消注释测试代码里的// foodStore.getSnack = jest.fn()后测试可通过,但这种写法不够优雅。想知道Pinia有没有官方的Action Mock方式,还是必须在Store实例创建后重新Mock Action?

错误信息

● useMamaStore › should make a snack if food store has snacks available

TypeError: foodStore.getSnack.mockReturnValue is not a function

    58 |   it('should make a snack if food store has snacks available', () => {
    59 |     // Mock food store to return a valid snack
  > 60 |     foodStore.getSnack.mockReturnValue(true)
       |                        ^
    61 |     mamaStore.makeSnack()
    62 |     expect(foodStore.getSnack).toHaveBeenCalled()
    63 |     expect(mamaStore.snackMade).toBe(true) 

    at Object.mockReturnValue
      (test/jest/__tests__/stores/mama.jest.spec.js:60:24)

测试代码

import { setActivePinia, createPinia, getActivePinia } from 'pinia'
import { useMamaStore } from 'src/stores/mama'
import { useFoodStore } from 'src/stores/food'
export const getStore = (name) => {
  const store = getActivePinia()?._s.get(name)
  if (!store) throw new Error(`${name} store does not exist`)
  return store
}

jest.mock(
  'src/stores/food',
  () => {
    const { defineStore } = require('pinia')
    const useFoodStore = defineStore('food', {
      state: () => ({ snacks: 10 }),
      actions: {
        getSnack: jest.fn(),
      },
    })
    return { useFoodStore }
  },
)
describe('useMamaStore', () => {
  let mamaStore
  let foodStore

  beforeEach(() => {
    const pinia = createPinia()
    setActivePinia(pinia)
    foodStore = useFoodStore(pinia)
    // foodStore.getSnack = jest.fn()
    mamaStore = useMamaStore(pinia)

    jest.clearAllMocks() 
  })


  it('should make a snack if food store has snacks available', () => {
    // Mock food store to return a valid snack
    foodStore.getSnack.mockReturnValue(true)
    mamaStore.makeSnack()
    expect(foodStore.getSnack).toHaveBeenCalled()
    expect(mamaStore.snackMade).toBe(true) 
  })

})

相关Store代码

src/stores/mama.js

import { defineStore } from 'pinia'
import { useFoodStore } from './food'

export const useMamaStore = defineStore('mama', {
  state: () => ({
    loving: true,
    caring: true,
    snackMade: false,
  }),
  getters: {
    timeForSnack(state) {
      return state.snackMade
    },
  },
  actions: {
    makeSnack() {
      const foodStore = useFoodStore()
      const snack = foodStore.getSnack()
      this.snackMade = snack
    },
  },
})

src/stores/food.js

import { defineStore } from 'pinia'

export const useFoodStore = defineStore('food', {
  state: {
    delicious: true,
    snacks: 10,
  },
  getters,
  actions: {
    getSnack() {
      if (this.snacks === 0) {
        return false
      }
      this.snacks = this.snacks - 1
      return true
    },
  },
})

解决方案

问题根源

你在jest.mock里定义的getSnack: jest.fn()会被Pinia做一层包装,绑定store的上下文,导致原来的jest mock函数实例被覆盖,无法访问mockReturnValue等方法。

方案一:暴露Mock函数到模块外部

修改jest.mock的实现,把mock函数单独赋值,再暴露出来,这样可以直接在测试中操作这个原始mock:

jest.mock(
  'src/stores/food',
  () => {
    const { defineStore } = require('pinia')
    // 单独定义mock函数
    const mockGetSnack = jest.fn()
    const useFoodStore = defineStore('food', {
      state: () => ({ snacks: 10 }),
      actions: {
        getSnack: mockGetSnack,
      },
    })
    // 暴露mock函数
    return { useFoodStore, mockGetSnack }
  },
)

测试中直接使用暴露的mock:

import { mockGetSnack } from 'src/stores/food'

it('should make a snack if food store has snacks available', () => {
  mockGetSnack.mockReturnValue(true)
  mamaStore.makeSnack()
  expect(mockGetSnack).toHaveBeenCalled()
  expect(mamaStore.snackMade).toBe(true) 
})

方案二:使用Pinia官方测试工具(推荐)

Pinia提供了@pinia/testing工具,可以自动处理action的mock,无需手动mock整个模块:

  1. 安装依赖:npm install -D @pinia/testing
  2. 修改测试代码:
import { setActivePinia } from 'pinia'
import { createTestingPinia } from '@pinia/testing'
import { useMamaStore } from 'src/stores/mama'
import { useFoodStore } from 'src/stores/food'

describe('useMamaStore', () => {
  let mamaStore
  let foodStore

  beforeEach(() => {
    // 创建测试用Pinia实例,自动mock所有actions
    const pinia = createTestingPinia({
      createSpy: jest.fn, // 指定用Jest创建mock函数
    })
    setActivePinia(pinia)
    foodStore = useFoodStore(pinia)
    mamaStore = useMamaStore(pinia)
  })

  it('should make a snack if food store has snacks available', () => {
    foodStore.getSnack.mockReturnValue(true)
    mamaStore.makeSnack()
    expect(foodStore.getSnack).toHaveBeenCalled()
    expect(mamaStore.snackMade).toBe(true) 
  })
})

这种方式会自动把所有store的action替换为Jest mock函数,同时保留store的其他逻辑,是Pinia官方推荐的测试方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:38:12