Vue 3 组件测试中模拟 Pinia 仓库应 mock store 工厂函数(如 useUserStore),返回可控的 mock 实例,确保组件访问的状态和方法可断言、可控制,无需 mock Pinia 核心 API。

在 Vue 3 组件测试中模拟 Pinia 仓库,核心是用 jest.mock 或 vi.mock(Vitest)替换真实 store 的导入,并手动构造一个符合接口的 mock 实例。关键不是“完全替代 Pinia”,而是让组件调用的 store 方法和状态可控制、可断言。
用 vi.mock 模拟 store 模块(推荐 Vitest)
Vitest 默认支持 ESM,mock Pinia store 最简洁的方式是直接 mock 对应的 store 文件模块:
- 假设你的 store 在
src/stores/user.ts,导出一个useUserStore工厂函数 - 在测试文件顶部 mock 它,返回一个带可控状态和方法的对象
- 确保 mock 返回的对象满足组件实际访问的属性(如
userInfo、login)
示例:
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
import { useUserStore } from '@/stores/user';
// ✅ Mock 整个 store 模块
vi.mock('@/stores/user', () => ({
useUserStore: vi.fn(() => ({
userInfo: { id: 1, name: 'Mock User' },
isLoggedIn: true,
login: vi.fn(),
logout: vi.fn()
}))
}));
describe('MyComponent', () => {
it('renders user name when logged in', async () => {
const wrapper = mount(MyComponent);
expect(wrapper.text()).toContain('Mock User');
// 断言方法是否被调用(如果组件内触发了 login)
const store = useUserStore();
await wrapper.vm.$nextTick();
expect(store.login).not.toHaveBeenCalled();
});
});
手动创建 mock store 并注入(更灵活,适合复杂逻辑)
当需要动态修改状态或验证多次调用时,不依赖自动 mock,而是显式创建 mock 实例并传入组件上下文:
立即学习“前端免费学习笔记(深入)”;
- 用
createPinia()创建真实 pinia 实例 - 用
vi.mock拦截 store 工厂函数,返回你手写的 mock 对象 - 或直接在
mount选项中通过global.provide注入 mock store(需配合pinia插件)
示例(手动注入):
import { createPinia } from 'pinia';
import { useUserStore } from '@/stores/user';
// Mock 工厂函数,返回可控制的实例
const mockStore = {
userInfo: { id: 99, name: 'Test User' },
isLoggedIn: false,
login: vi.fn(),
logout: vi.fn()
};
vi.mock('@/stores/user', () => ({
useUserStore: vi.fn(() => mockStore)
}));
it('calls login when button clicked', async () => {
const wrapper = mount(MyComponent, {
global: {
plugins: [createPinia()] // 确保 Pinia 插件可用(即使没真正用)
}
});
await wrapper.find('button').trigger('click');
expect(mockStore.login).toHaveBeenCalledTimes(1);
});
注意:避免 mock Pinia 核心(如 createPinia)本身
不需要 mock pinia 包或 createPinia —— 它们只是容器。重点 mock **你的 store 工厂函数**(如 useUserStore),因为组件只依赖它返回的对象结构。mock 核心 API 反而容易破坏内部机制,导致测试不稳定。
- ✅ 正确:mock
useUserStore,返回精简对象 - ❌ 不必要:mock
pinia或defineStore - ⚠️ 注意:若 store 内部用了
onMounted或异步初始化,mock 中可忽略或空实现
配合组件 props 或事件做状态驱动测试
有时组件行为由 store 状态驱动,但你希望隔离测试逻辑。这时可结合 mock store + props 控制输入:
- 比如组件根据
store.isLoggedIn渲染不同内容 → 直接改 mock 对象的isLoggedIn值 - 再
wrapper.rerender()或重新 mount 触发响应式更新 - 避免在测试中调用
store.$patch等真实逻辑,保持 mock 纯净
例如:
// 修改 mock 状态后触发重渲染
mockStore.isLoggedIn = false;
await wrapper.vm.$nextTick();
expect(wrapper.text()).not.toContain('Dashboard');


















