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

