Vue 3 + Vitest环境下Pinia依赖注入测试问题求解
解决Vitest测试Pinia时inject报错的问题
问题原因
你的Pinia store里直接调用inject(),但测试时如果没有先创建Vue应用上下文、激活Pinia并提供依赖,inject就会因为不在setup上下文里报错,同时拿不到repo导致undefined。
解决方案
方案1:测试前创建Vue上下文并提供依赖
在测试的beforeEach钩子中,手动创建Vue应用、提供测试用的MyRepo实例,再激活Pinia,这样store调用inject时就能拿到依赖:
import { createApp } from 'vue'; import { createPinia, setActivePinia } from 'pinia'; import { useMyStore } from './myStore'; // 导入你的测试用假仓库 import { MyFakeRepo } from './MyFakeRepo'; describe('myStore', () => { beforeEach(() => { // 创建测试用Vue应用 const app = createApp({}); // 提供测试用的仓库实例 app.provide('myRepo', new MyFakeRepo()); // 创建并激活Pinia const pinia = createPinia(); app.use(pinia); setActivePinia(pinia); }); it('能正常使用假仓库执行逻辑', () => { const store = useMyStore(); // 这里编写你的测试断言 expect(store.someData).toBe('fake-data'); }); });
方案2:通过Pinia插件传递依赖(更解耦)
修改你的插件,同时作为Pinia插件把repo挂载到store实例上,避免store直接依赖inject:
1. 修改MyPlugin.ts
import type { PiniaPluginContext } from 'pinia'; import { MyRepo, MyFakeRepo } from './repos'; export function createMyRepoPlugin(isTest = false) { const repo = isTest ? new MyFakeRepo() : new MyRepo(); return { install(app) { // 保留原有的app.provide供其他组件使用 app.provide('myRepo', repo); // 注册Pinia插件,把repo挂载到每个store实例 app.use(createPinia().use((context: PiniaPluginContext) => { context.store.$repo = repo; })); } }; }
2. 修改myStore.ts
import { defineStore, getCurrentInstance } from 'pinia'; import type { MyRepoInterface } from './MyRepoInterface'; defineStore('myStore', () => { // 从当前store实例获取挂载的repo const store = getCurrentInstance()?.proxy as ReturnType<typeof useMyStore>; const repo = store?.$repo as MyRepoInterface; // 在actions中直接使用this.$repo const fetchData = async () => { const data = await repo.fetchData(); // 处理数据逻辑 }; return { fetchData }; });
3. 测试时使用插件的测试模式
import { createApp } from 'vue'; import { createPinia, setActivePinia } from 'pinia'; import { useMyStore } from './myStore'; import { createMyRepoPlugin } from './MyPlugin'; describe('myStore', () => { beforeEach(() => { const app = createApp({}); // 传入true启用测试模式,使用假仓库 app.use(createMyRepoPlugin(true)); const pinia = createPinia(); app.use(pinia); setActivePinia(pinia); }); // 测试用例... });
方案3:完全脱离Vue上下文的工厂模式(最灵活)
把仓库作为参数传入store的定义,让store彻底独立于Vue的依赖注入:
1. 修改myStore.ts
import { defineStore } from 'pinia'; import type { MyRepoInterface } from './MyRepoInterface'; // 定义一个工厂函数,接收repo作为参数 export function createMyStore(repo: MyRepoInterface) { return defineStore('myStore', () => { const fetchData = async () => { return await repo.fetchData(); }; return { fetchData }; })(); }
2. 运行时使用
// main.ts或单独的store入口文件 import { MyRepo } from './MyRepo'; export const useMyStore = createMyStore(new MyRepo());
3. 测试时使用
import { createPinia, setActivePinia } from 'pinia'; import { createMyStore } from './myStore'; import { MyFakeRepo } from './MyFakeRepo'; describe('myStore', () => { beforeEach(() => { setActivePinia(createPinia()); }); it('使用假仓库正常工作', () => { const testStore = createMyStore(new MyFakeRepo()); // 测试逻辑... }); });
对当前依赖注入方式的优化建议
- 避免store直接依赖Vue的inject:你的当前写法让Pinia store和Vue的依赖注入上下文强绑定,导致脱离Vue环境(比如测试时直接调用useStore)就会报错,耦合度太高,不利于单独测试store。
- 优先用Pinia插件传递依赖:Pinia本身提供了插件机制,可以把全局依赖挂载到store实例上,这样store不需要依赖Vue的inject API,更符合Pinia的设计逻辑。
- 工厂模式更适合独立测试:如果你的store不需要和Vue组件强绑定,用工厂函数传入依赖的方式最灵活,测试时完全不需要处理Vue上下文,直接传入假依赖即可。
内容的提问来源于stack exchange,提问作者Fayçal Borsali
相关产品推荐
相关产品推荐

