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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 20:14:54