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

Vitest单元测试中如何Mock组合式函数依赖的Pinia Store?

解决方案:验证Pinia Store Action调用的正确测试方式

针对你遇到的三种Mock失败情况,下面给出对应的修复方案及最优实践:

一、修复createTestingPinia方案(官方推荐)

你的第三种方式失败大概率是组合式函数初始化晚于Store创建,或者没有正确注入测试Pinia。正确步骤如下:

import { createTestingPinia } from '@pinia/testing'
import { setActivePinia } from 'pinia'
import useYourComposable from '@/composables/your-composable'
import useResponseStore from '@/stores/responseStore'

describe('Your Composable', () => {
  let respStore: ReturnType<typeof useResponseStore>

  beforeEach(() => {
    // 1. 创建测试Pinia,用vi.fn创建spy
    const pinia = createTestingPinia({
      createSpy: vi.fn,
      fakeApp: true
    })
    // 2. 设置全局激活的Pinia
    setActivePinia(pinia)
    // 3. 获取Store实例(此时action已被包装为spy)
    respStore = useResponseStore()
    // 4. 初始化组合式函数(必须在Pinia设置完成后)
    useYourComposable()
  })

  it('should call updateResponse', () => {
    // 触发组合式函数中调用updateResponse的逻辑
    // 示例:yourComposable.triggerAction()
    
    // 断言spy调用次数
    expect(respStore.updateResponse).toBeCalledTimes(1)
  })
})

关键注意点:

  • 必须在组合式函数初始化前完成setActivePinia和Store实例的创建
  • 如果组合式函数的逻辑是按需触发(比如需要调用某个方法才会执行action),测试中要主动触发该逻辑

二、修复vi.mock的引用初始化问题(第二种方式的解决)

针对vi.mock提升导致的变量未初始化错误,有两种可行修复方式:

方案A:在mock内部创建spy

把spy定义在mock函数内部,避免变量提升导致的引用问题:

import useResponseStore from '@/stores/responseStore'
import useYourComposable from '@/composables/your-composable'

vi.mock('@/stores/responseStore', () => {
  const mockUpdateResponse = vi.fn()
  const mockClearResponse = vi.fn()
  const mockUpdatePackage = vi.fn()

  return {
    default: vi.fn(() => ({
      updateResponse: mockUpdateResponse,
      clearResponse: mockClearResponse,
      updateResponsePackages: mockUpdatePackage
    }))
  }
})

describe('Your Composable', () => {
  it('should call updateResponse', () => {
    useYourComposable()
    const respStore = useResponseStore()
    
    // 触发逻辑后断言
    expect(respStore.updateResponse).toBeCalledTimes(1)
  })
})

方案B:使用vi.doMock避免提升

vi.doMock不会被提升到文件顶部,可以在变量初始化后再执行mock:

describe('Your Composable', () => {
  it('should call updateResponse', async () => {
    const mockUpdateResponse = vi.fn()
    
    // vi.doMock不会被提升,此时变量已初始化
    await vi.doMock('@/stores/responseStore', () => ({
      default: vi.fn(() => ({
        updateResponse: mockUpdateResponse
      }))
    }))

    // 动态导入组合式函数(必须在doMock之后)
    const { default: useYourComposable } = await import('@/composables/your-composable')
    useYourComposable()

    expect(mockUpdateResponse).toBeCalledTimes(1)
  })
})

三、修复部分mock方案(第一种方式的解决)

如果需要保留Store的其他逻辑,只mock特定action,要确保正确匹配Store的导出结构,比如是否为默认导出、函数名是否正确:

假设你的responseStore.ts定义如下:

// responseStore.ts
export const useResponseStore = defineStore('response', {
  actions: {
    updateResponse() { /* 原逻辑 */ }
  }
})

那么部分mock的正确写法:

const mockUpdateResponse = vi.fn()

vi.mock('@/stores/responseStore', async () => {
  const actual = await vi.importActual('@/stores/responseStore')
  return {
    ...actual,
    useResponseStore: vi.fn(() => ({
      // 保留原Store的其他属性,只替换updateResponse
      ...actual.useResponseStore(),
      updateResponse: mockUpdateResponse
    }))
  }
})

describe('Your Composable', () => {
  it('should call updateResponse', () => {
    useYourComposable()
    expect(mockUpdateResponse).toBeCalledTimes(1)
  })
})

关键注意点:

  • 确保mock的函数名和Store导出的一致(你第一种方式里可能写错了函数名,比如把useResponseStore写成了userResponseStore)
  • 用...actual.useResponseStore()保留原Store的其他属性,避免组合式函数调用其他方法时出错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:07:29