如何在Pinia+Vue测试中模拟Store引用的全局变量gapi?
问题描述
我开发的Vue SPA使用Pinia做状态管理,通过Google API脚本标签在全局注入了gapi对象,Store代码中直接调用了gapi的方法:
function gapiLoaded() { gapi.load('client', initializeGapiClient); }
但在使用@pinia/testing和vue-test-utils测试组件时,Store代码执行会触发gapi is not defined错误。尝试过用vue-test-utils的global.mocks模拟gapi,但该方法仅对组件生效,无法覆盖Store中的全局变量引用。
解决方案
方法1:在测试环境全局挂载Mock对象
由于Store直接访问全局gapi,需在测试运行前将Mock对象挂载到全局作用域。在Vitest中可直接操作global或window:
// 测试文件顶部 import { vi } from 'vitest'; // 全局挂载Mock的gapi global.gapi = { load: vi.fn((_, callback) => callback()), // 可选:直接触发回调模拟加载完成 client: { init: vi.fn().mockResolvedValue({}) } };
若所有测试都需要该Mock,可配置到Vitest的setupFiles中:
在vitest.config.ts添加:
export default defineConfig({ test: { setupFiles: ['./tests/setupGlobalMocks.ts'] } });
然后在setupGlobalMocks.ts中定义全局Mock:
import { vi } from 'vitest'; global.gapi = { load: vi.fn((_, callback) => callback()), client: { init: vi.fn().mockResolvedValue({}) } };
方法2:重构Store,封装全局依赖
通过封装gapi的获取逻辑,让Store不再直接依赖全局变量,便于测试时Mock:
1. 封装gapi获取工具
// src/utils/gapi.ts export const getGapi = () => { return typeof window !== 'undefined' ? window.gapi : null; };
2. Store中使用封装后的方法
// src/stores/drive.ts import { getGapi } from '@/utils/gapi'; function gapiLoaded() { const gapi = getGapi(); if (!gapi) return; gapi.load('client', initializeGapiClient); }
3. 测试时Mock封装函数
// 测试文件中 import { getGapi } from '@/utils/gapi'; import { vi } from 'vitest'; // Mock整个工具模块 vi.mock('@/utils/gapi', () => ({ getGapi: vi.fn(() => ({ load: vi.fn(), client: { init: vi.fn() } })) }));
方法3:通过Vue依赖注入传递gapi
将gapi作为依赖注入到Store,测试时替换为Mock对象:
1. Store中通过inject获取gapi
// src/stores/drive.ts import { defineStore } from 'pinia'; import { inject } from 'vue'; export const useDriveStore = defineStore('drive', () => { // 从Vue的依赖注入中获取gapi const gapi = inject('gapi'); function gapiLoaded() { gapi?.load('client', initializeGapiClient); } return { gapiLoaded }; });
2. 生产环境全局provide gapi
在main.ts中添加:
import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); // 全局提供gapi app.provide('gapi', window.gapi); // ...其他配置(Pinia、路由等) app.mount('#app');
3. 测试时提供Mock的gapi
// 测试文件中 import { describe, it, expect, vi } from 'vitest'; import { mount, provide } from '@vue/test-utils'; import Files from '../Files.vue'; import { createTestingPinia } from '@pinia/testing'; import { useDriveStore } from '@/stores/drive'; describe('Files Component', () => { it('renders properly', () => { const wrapper = mount(Files, { global: { plugins: [ createTestingPinia({ createSpy: vi.fn, initialState: { drive: { files: [ { name: 'test file 1', id: '1' }, { name: 'test file 2', id: '2' } ] } } }) ], // 提供Mock的gapi providers: [ provide('gapi', { load: vi.fn(), client: { init: vi.fn() } }) ] } }); const store = useDriveStore(); const filesList = '[data-testid=files-list]'; expect(store.fetchFiles).toHaveBeenCalledTimes(1); expect(wrapper.find(filesList).text()).toContain('test file 1'); expect(wrapper.findAll('[data-testid=files-item]')).toHaveLength(2); }); });
内容的提问来源于stack exchange,提问作者Caleb Jay
相关产品推荐
相关产品推荐

